empty mask on tables
[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 {string} empty_results  Text to display for no results 
9141  * @cfg {boolean} striped Should the rows be alternative striped
9142  * @cfg {boolean} bordered Add borders to the table
9143  * @cfg {boolean} hover Add hover highlighting
9144  * @cfg {boolean} condensed Format condensed
9145  * @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,
9146  *                also adds table-responsive (see bootstrap docs for details)
9147  * @cfg {Boolean} loadMask (true|false) default false
9148  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9149  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9150  * @cfg {Boolean} rowSelection (true|false) default false
9151  * @cfg {Boolean} cellSelection (true|false) default false
9152  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9153  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9154  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9155  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9156  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9157  *
9158  * 
9159  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9160  * 
9161  * @constructor
9162  * Create a new Table
9163  * @param {Object} config The config object
9164  */
9165
9166 Roo.bootstrap.Table = function(config)
9167 {
9168     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9169      
9170     // BC...
9171     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9172     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9173     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9174     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9175     
9176     this.view = this; // compat with grid.
9177     
9178     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9179     if (this.sm) {
9180         this.sm.grid = this;
9181         this.selModel = Roo.factory(this.sm, Roo.grid);
9182         this.sm = this.selModel;
9183         this.sm.xmodule = this.xmodule || false;
9184     }
9185     
9186     if (this.cm && typeof(this.cm.config) == 'undefined') {
9187         this.colModel = new Roo.grid.ColumnModel(this.cm);
9188         this.cm = this.colModel;
9189         this.cm.xmodule = this.xmodule || false;
9190     }
9191     if (this.store) {
9192         this.store= Roo.factory(this.store, Roo.data);
9193         this.ds = this.store;
9194         this.ds.xmodule = this.xmodule || false;
9195          
9196     }
9197     if (this.footer && this.store) {
9198         this.footer.dataSource = this.ds;
9199         this.footer = Roo.factory(this.footer);
9200     }
9201     
9202     /** @private */
9203     this.addEvents({
9204         /**
9205          * @event cellclick
9206          * Fires when a cell is clicked
9207          * @param {Roo.bootstrap.Table} this
9208          * @param {Roo.Element} el
9209          * @param {Number} rowIndex
9210          * @param {Number} columnIndex
9211          * @param {Roo.EventObject} e
9212          */
9213         "cellclick" : true,
9214         /**
9215          * @event celldblclick
9216          * Fires when a cell is double clicked
9217          * @param {Roo.bootstrap.Table} this
9218          * @param {Roo.Element} el
9219          * @param {Number} rowIndex
9220          * @param {Number} columnIndex
9221          * @param {Roo.EventObject} e
9222          */
9223         "celldblclick" : true,
9224         /**
9225          * @event rowclick
9226          * Fires when a row is clicked
9227          * @param {Roo.bootstrap.Table} this
9228          * @param {Roo.Element} el
9229          * @param {Number} rowIndex
9230          * @param {Roo.EventObject} e
9231          */
9232         "rowclick" : true,
9233         /**
9234          * @event rowdblclick
9235          * Fires when a row is double clicked
9236          * @param {Roo.bootstrap.Table} this
9237          * @param {Roo.Element} el
9238          * @param {Number} rowIndex
9239          * @param {Roo.EventObject} e
9240          */
9241         "rowdblclick" : true,
9242         /**
9243          * @event mouseover
9244          * Fires when a mouseover occur
9245          * @param {Roo.bootstrap.Table} this
9246          * @param {Roo.Element} el
9247          * @param {Number} rowIndex
9248          * @param {Number} columnIndex
9249          * @param {Roo.EventObject} e
9250          */
9251         "mouseover" : true,
9252         /**
9253          * @event mouseout
9254          * Fires when a mouseout occur
9255          * @param {Roo.bootstrap.Table} this
9256          * @param {Roo.Element} el
9257          * @param {Number} rowIndex
9258          * @param {Number} columnIndex
9259          * @param {Roo.EventObject} e
9260          */
9261         "mouseout" : true,
9262         /**
9263          * @event rowclass
9264          * Fires when a row is rendered, so you can change add a style to it.
9265          * @param {Roo.bootstrap.Table} this
9266          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9267          */
9268         'rowclass' : true,
9269           /**
9270          * @event rowsrendered
9271          * Fires when all the  rows have been rendered
9272          * @param {Roo.bootstrap.Table} this
9273          */
9274         'rowsrendered' : true,
9275         /**
9276          * @event contextmenu
9277          * The raw contextmenu event for the entire grid.
9278          * @param {Roo.EventObject} e
9279          */
9280         "contextmenu" : true,
9281         /**
9282          * @event rowcontextmenu
9283          * Fires when a row is right clicked
9284          * @param {Roo.bootstrap.Table} this
9285          * @param {Number} rowIndex
9286          * @param {Roo.EventObject} e
9287          */
9288         "rowcontextmenu" : true,
9289         /**
9290          * @event cellcontextmenu
9291          * Fires when a cell is right clicked
9292          * @param {Roo.bootstrap.Table} this
9293          * @param {Number} rowIndex
9294          * @param {Number} cellIndex
9295          * @param {Roo.EventObject} e
9296          */
9297          "cellcontextmenu" : true,
9298          /**
9299          * @event headercontextmenu
9300          * Fires when a header is right clicked
9301          * @param {Roo.bootstrap.Table} this
9302          * @param {Number} columnIndex
9303          * @param {Roo.EventObject} e
9304          */
9305         "headercontextmenu" : true,
9306         /**
9307          * @event mousedown
9308          * The raw mousedown event for the entire grid.
9309          * @param {Roo.EventObject} e
9310          */
9311         "mousedown" : true
9312         
9313     });
9314 };
9315
9316 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9317     
9318     cls: false,
9319     
9320     empty_results : '',
9321     striped : false,
9322     scrollBody : false,
9323     bordered: false,
9324     hover:  false,
9325     condensed : false,
9326     responsive : false,
9327     sm : false,
9328     cm : false,
9329     store : false,
9330     loadMask : false,
9331     footerShow : true,
9332     headerShow : true,
9333     enableColumnResize: true,
9334   
9335     rowSelection : false,
9336     cellSelection : false,
9337     layout : false,
9338
9339     minColumnWidth : 50,
9340     
9341     // Roo.Element - the tbody
9342     bodyEl: false,  // <tbody> Roo.Element - thead element    
9343     headEl: false,  // <thead> Roo.Element - thead element
9344     resizeProxy : false, // proxy element for dragging?
9345
9346
9347     
9348     container: false, // used by gridpanel...
9349     
9350     lazyLoad : false,
9351     
9352     CSS : Roo.util.CSS,
9353     
9354     auto_hide_footer : false,
9355     
9356     view: false, // actually points to this..
9357     
9358     getAutoCreate : function()
9359     {
9360         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9361         
9362         cfg = {
9363             tag: 'table',
9364             cls : 'table', 
9365             cn : []
9366         };
9367         // this get's auto added by panel.Grid
9368         if (this.scrollBody) {
9369             cfg.cls += ' table-body-fixed';
9370         }    
9371         if (this.striped) {
9372             cfg.cls += ' table-striped';
9373         }
9374         
9375         if (this.hover) {
9376             cfg.cls += ' table-hover';
9377         }
9378         if (this.bordered) {
9379             cfg.cls += ' table-bordered';
9380         }
9381         if (this.condensed) {
9382             cfg.cls += ' table-condensed';
9383         }
9384         
9385         if (this.responsive) {
9386             cfg.cls += ' table-responsive';
9387         }
9388         
9389         if (this.cls) {
9390             cfg.cls+=  ' ' +this.cls;
9391         }
9392         
9393         
9394         
9395         if (this.layout) {
9396             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9397         }
9398         
9399         if(this.store || this.cm){
9400             if(this.headerShow){
9401                 cfg.cn.push(this.renderHeader());
9402             }
9403             
9404             cfg.cn.push(this.renderBody());
9405             
9406             if(this.footerShow){
9407                 cfg.cn.push(this.renderFooter());
9408             }
9409             // where does this come from?
9410             //cfg.cls+=  ' TableGrid';
9411         }
9412         
9413         return { cn : [ cfg ] };
9414     },
9415     
9416     initEvents : function()
9417     {   
9418         if(!this.store || !this.cm){
9419             return;
9420         }
9421         if (this.selModel) {
9422             this.selModel.initEvents();
9423         }
9424         
9425         
9426         //Roo.log('initEvents with ds!!!!');
9427         
9428         this.bodyEl = this.el.select('tbody', true).first();
9429         this.headEl = this.el.select('thead', true).first();
9430         this.mainFoot = this.el.select('tfoot', true).first();
9431         
9432         
9433         
9434         
9435         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9436             e.on('click', this.sort, this);
9437         }, this);
9438         
9439         
9440         // why is this done????? = it breaks dialogs??
9441         //this.parent().el.setStyle('position', 'relative');
9442         
9443         
9444         if (this.footer) {
9445             this.footer.parentId = this.id;
9446             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9447             
9448             if(this.lazyLoad){
9449                 this.el.select('tfoot tr td').first().addClass('hide');
9450             }
9451         } 
9452         
9453         if(this.loadMask) {
9454             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9455         }
9456         
9457         this.store.on('load', this.onLoad, this);
9458         this.store.on('beforeload', this.onBeforeLoad, this);
9459         this.store.on('update', this.onUpdate, this);
9460         this.store.on('add', this.onAdd, this);
9461         this.store.on("clear", this.clear, this);
9462         
9463         this.el.on("contextmenu", this.onContextMenu, this);
9464         
9465         
9466         this.cm.on("headerchange", this.onHeaderChange, this);
9467         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9468
9469  //?? does bodyEl get replaced on render?
9470         this.bodyEl.on("click", this.onClick, this);
9471         this.bodyEl.on("dblclick", this.onDblClick, this);        
9472         this.bodyEl.on('scroll', this.onBodyScroll, this);
9473
9474         // guessing mainbody will work - this relays usually caught by selmodel at present.
9475         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9476   
9477   
9478         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9479         
9480   
9481         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9482             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9483         }
9484         
9485         this.initCSS();
9486     },
9487     // Compatibility with grid - we implement all the view features at present.
9488     getView : function()
9489     {
9490         return this;
9491     },
9492     
9493     initCSS : function()
9494     {
9495         
9496         
9497         var cm = this.cm, styles = [];
9498         this.CSS.removeStyleSheet(this.id + '-cssrules');
9499         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9500         // we can honour xs/sm/md/xl  as widths...
9501         // we first have to decide what widht we are currently at...
9502         var sz = Roo.getGridSize();
9503         
9504         var total = 0;
9505         var last = -1;
9506         var cols = []; // visable cols.
9507         var total_abs = 0;
9508         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9509             var w = cm.getColumnWidth(i, false);
9510             if(cm.isHidden(i)){
9511                 cols.push( { rel : false, abs : 0 });
9512                 continue;
9513             }
9514             if (w !== false) {
9515                 cols.push( { rel : false, abs : w });
9516                 total_abs += w;
9517                 last = i; // not really..
9518                 continue;
9519             }
9520             var w = cm.getColumnWidth(i, sz);
9521             if (w > 0) {
9522                 last = i
9523             }
9524             total += w;
9525             cols.push( { rel : w, abs : false });
9526         }
9527         
9528         var avail = this.bodyEl.dom.clientWidth - total_abs;
9529         
9530         var unitWidth = Math.floor(avail / total);
9531         var rem = avail - (unitWidth * total);
9532         
9533         var hidden, width, pos = 0 , splithide , left;
9534         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9535             
9536             hidden = 'display:none;';
9537             left = '';
9538             width  = 'width:0px;';
9539             splithide = '';
9540             if(!cm.isHidden(i)){
9541                 hidden = '';
9542                 
9543                 
9544                 // we can honour xs/sm/md/xl ?
9545                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9546                 if (w===0) {
9547                     hidden = 'display:none;';
9548                 }
9549                 // width should return a small number...
9550                 if (i == last) {
9551                     w+=rem; // add the remaining with..
9552                 }
9553                 pos += w;
9554                 left = "left:" + (pos -4) + "px;";
9555                 width = "width:" + w+ "px;";
9556                 
9557             }
9558             if (this.responsive) {
9559                 width = '';
9560                 left = '';
9561                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9562                 splithide = 'display: none;';
9563             }
9564             
9565             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9566             if (this.headEl) {
9567                 if (i == last) {
9568                     splithide = 'display:none;';
9569                 }
9570                 
9571                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9572                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9573                             // this is the popover version..
9574                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9575                 );
9576             }
9577             
9578         }
9579         //Roo.log(styles.join(''));
9580         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9581         
9582     },
9583     
9584     
9585     
9586     onContextMenu : function(e, t)
9587     {
9588         this.processEvent("contextmenu", e);
9589     },
9590     
9591     processEvent : function(name, e)
9592     {
9593         if (name != 'touchstart' ) {
9594             this.fireEvent(name, e);    
9595         }
9596         
9597         var t = e.getTarget();
9598         
9599         var cell = Roo.get(t);
9600         
9601         if(!cell){
9602             return;
9603         }
9604         
9605         if(cell.findParent('tfoot', false, true)){
9606             return;
9607         }
9608         
9609         if(cell.findParent('thead', false, true)){
9610             
9611             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9612                 cell = Roo.get(t).findParent('th', false, true);
9613                 if (!cell) {
9614                     Roo.log("failed to find th in thead?");
9615                     Roo.log(e.getTarget());
9616                     return;
9617                 }
9618             }
9619             
9620             var cellIndex = cell.dom.cellIndex;
9621             
9622             var ename = name == 'touchstart' ? 'click' : name;
9623             this.fireEvent("header" + ename, this, cellIndex, e);
9624             
9625             return;
9626         }
9627         
9628         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9629             cell = Roo.get(t).findParent('td', false, true);
9630             if (!cell) {
9631                 Roo.log("failed to find th in tbody?");
9632                 Roo.log(e.getTarget());
9633                 return;
9634             }
9635         }
9636         
9637         var row = cell.findParent('tr', false, true);
9638         var cellIndex = cell.dom.cellIndex;
9639         var rowIndex = row.dom.rowIndex - 1;
9640         
9641         if(row !== false){
9642             
9643             this.fireEvent("row" + name, this, rowIndex, e);
9644             
9645             if(cell !== false){
9646             
9647                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9648             }
9649         }
9650         
9651     },
9652     
9653     onMouseover : function(e, el)
9654     {
9655         var cell = Roo.get(el);
9656         
9657         if(!cell){
9658             return;
9659         }
9660         
9661         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9662             cell = cell.findParent('td', false, true);
9663         }
9664         
9665         var row = cell.findParent('tr', false, true);
9666         var cellIndex = cell.dom.cellIndex;
9667         var rowIndex = row.dom.rowIndex - 1; // start from 0
9668         
9669         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9670         
9671     },
9672     
9673     onMouseout : function(e, el)
9674     {
9675         var cell = Roo.get(el);
9676         
9677         if(!cell){
9678             return;
9679         }
9680         
9681         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9682             cell = cell.findParent('td', false, true);
9683         }
9684         
9685         var row = cell.findParent('tr', false, true);
9686         var cellIndex = cell.dom.cellIndex;
9687         var rowIndex = row.dom.rowIndex - 1; // start from 0
9688         
9689         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9690         
9691     },
9692     
9693     onClick : function(e, el)
9694     {
9695         var cell = Roo.get(el);
9696         
9697         if(!cell || (!this.cellSelection && !this.rowSelection)){
9698             return;
9699         }
9700         
9701         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9702             cell = cell.findParent('td', false, true);
9703         }
9704         
9705         if(!cell || typeof(cell) == 'undefined'){
9706             return;
9707         }
9708         
9709         var row = cell.findParent('tr', false, true);
9710         
9711         if(!row || typeof(row) == 'undefined'){
9712             return;
9713         }
9714         
9715         var cellIndex = cell.dom.cellIndex;
9716         var rowIndex = this.getRowIndex(row);
9717         
9718         // why??? - should these not be based on SelectionModel?
9719         //if(this.cellSelection){
9720             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9721         //}
9722         
9723         //if(this.rowSelection){
9724             this.fireEvent('rowclick', this, row, rowIndex, e);
9725         //}
9726          
9727     },
9728         
9729     onDblClick : function(e,el)
9730     {
9731         var cell = Roo.get(el);
9732         
9733         if(!cell || (!this.cellSelection && !this.rowSelection)){
9734             return;
9735         }
9736         
9737         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9738             cell = cell.findParent('td', false, true);
9739         }
9740         
9741         if(!cell || typeof(cell) == 'undefined'){
9742             return;
9743         }
9744         
9745         var row = cell.findParent('tr', false, true);
9746         
9747         if(!row || typeof(row) == 'undefined'){
9748             return;
9749         }
9750         
9751         var cellIndex = cell.dom.cellIndex;
9752         var rowIndex = this.getRowIndex(row);
9753         
9754         if(this.cellSelection){
9755             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9756         }
9757         
9758         if(this.rowSelection){
9759             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9760         }
9761     },
9762     findRowIndex : function(el)
9763     {
9764         var cell = Roo.get(el);
9765         if(!cell) {
9766             return false;
9767         }
9768         var row = cell.findParent('tr', false, true);
9769         
9770         if(!row || typeof(row) == 'undefined'){
9771             return false;
9772         }
9773         return this.getRowIndex(row);
9774     },
9775     sort : function(e,el)
9776     {
9777         var col = Roo.get(el);
9778         
9779         if(!col.hasClass('sortable')){
9780             return;
9781         }
9782         
9783         var sort = col.attr('sort');
9784         var dir = 'ASC';
9785         
9786         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9787             dir = 'DESC';
9788         }
9789         
9790         this.store.sortInfo = {field : sort, direction : dir};
9791         
9792         if (this.footer) {
9793             Roo.log("calling footer first");
9794             this.footer.onClick('first');
9795         } else {
9796         
9797             this.store.load({ params : { start : 0 } });
9798         }
9799     },
9800     
9801     renderHeader : function()
9802     {
9803         var header = {
9804             tag: 'thead',
9805             cn : []
9806         };
9807         
9808         var cm = this.cm;
9809         this.totalWidth = 0;
9810         
9811         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9812             
9813             var config = cm.config[i];
9814             
9815             var c = {
9816                 tag: 'th',
9817                 cls : 'x-hcol-' + i,
9818                 style : '',
9819                 
9820                 html: cm.getColumnHeader(i)
9821             };
9822             
9823             var tooltip = cm.getColumnTooltip(i);
9824             if (tooltip) {
9825                 c.tooltip = tooltip;
9826             }
9827             
9828             
9829             var hh = '';
9830             
9831             if(typeof(config.sortable) != 'undefined' && config.sortable){
9832                 c.cls += ' sortable';
9833                 c.html = '<i class="fa"></i>' + c.html;
9834             }
9835             
9836             // could use BS4 hidden-..-down 
9837             
9838             if(typeof(config.lgHeader) != 'undefined'){
9839                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9840             }
9841             
9842             if(typeof(config.mdHeader) != 'undefined'){
9843                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9844             }
9845             
9846             if(typeof(config.smHeader) != 'undefined'){
9847                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9848             }
9849             
9850             if(typeof(config.xsHeader) != 'undefined'){
9851                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9852             }
9853             
9854             if(hh.length){
9855                 c.html = hh;
9856             }
9857             
9858             if(typeof(config.tooltip) != 'undefined'){
9859                 c.tooltip = config.tooltip;
9860             }
9861             
9862             if(typeof(config.colspan) != 'undefined'){
9863                 c.colspan = config.colspan;
9864             }
9865             
9866             // hidden is handled by CSS now
9867             
9868             if(typeof(config.dataIndex) != 'undefined'){
9869                 c.sort = config.dataIndex;
9870             }
9871             
9872            
9873             
9874             if(typeof(config.align) != 'undefined' && config.align.length){
9875                 c.style += ' text-align:' + config.align + ';';
9876             }
9877             
9878             /* width is done in CSS
9879              *if(typeof(config.width) != 'undefined'){
9880                 c.style += ' width:' + config.width + 'px;';
9881                 this.totalWidth += config.width;
9882             } else {
9883                 this.totalWidth += 100; // assume minimum of 100 per column?
9884             }
9885             */
9886             
9887             if(typeof(config.cls) != 'undefined'){
9888                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9889             }
9890             // this is the bit that doesnt reall work at all...
9891             
9892             if (this.responsive) {
9893                  
9894             
9895                 ['xs','sm','md','lg'].map(function(size){
9896                     
9897                     if(typeof(config[size]) == 'undefined'){
9898                         return;
9899                     }
9900                      
9901                     if (!config[size]) { // 0 = hidden
9902                         // BS 4 '0' is treated as hide that column and below.
9903                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9904                         return;
9905                     }
9906                     
9907                     c.cls += ' col-' + size + '-' + config[size] + (
9908                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9909                     );
9910                     
9911                     
9912                 });
9913             }
9914             // at the end?
9915             
9916             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9917             
9918             
9919             
9920             
9921             header.cn.push(c)
9922         }
9923         
9924         return header;
9925     },
9926     
9927     renderBody : function()
9928     {
9929         var body = {
9930             tag: 'tbody',
9931             cn : [
9932                 {
9933                     tag: 'tr',
9934                     cn : [
9935                         {
9936                             tag : 'td',
9937                             colspan :  this.cm.getColumnCount()
9938                         }
9939                     ]
9940                 }
9941             ]
9942         };
9943         
9944         return body;
9945     },
9946     
9947     renderFooter : function()
9948     {
9949         var footer = {
9950             tag: 'tfoot',
9951             cn : [
9952                 {
9953                     tag: 'tr',
9954                     cn : [
9955                         {
9956                             tag : 'td',
9957                             colspan :  this.cm.getColumnCount()
9958                         }
9959                     ]
9960                 }
9961             ]
9962         };
9963         
9964         return footer;
9965     },
9966     
9967     
9968     
9969     onLoad : function()
9970     {
9971 //        Roo.log('ds onload');
9972         this.clear();
9973         
9974         var _this = this;
9975         var cm = this.cm;
9976         var ds = this.store;
9977         
9978         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9979             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9980             if (_this.store.sortInfo) {
9981                     
9982                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9983                     e.select('i', true).addClass(['fa-arrow-up']);
9984                 }
9985                 
9986                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9987                     e.select('i', true).addClass(['fa-arrow-down']);
9988                 }
9989             }
9990         });
9991         
9992         var tbody =  this.bodyEl;
9993               
9994         if(ds.getCount() > 0){
9995             ds.data.each(function(d,rowIndex){
9996                 var row =  this.renderRow(cm, ds, rowIndex);
9997                 
9998                 tbody.createChild(row);
9999                 
10000                 var _this = this;
10001                 
10002                 if(row.cellObjects.length){
10003                     Roo.each(row.cellObjects, function(r){
10004                         _this.renderCellObject(r);
10005                     })
10006                 }
10007                 
10008             }, this);
10009         } else if (this.empty_results.length) {
10010             this.el.mask(this.empty_results, 'no-spinner');
10011         }
10012         
10013         var tfoot = this.el.select('tfoot', true).first();
10014         
10015         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10016             
10017             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10018             
10019             var total = this.ds.getTotalCount();
10020             
10021             if(this.footer.pageSize < total){
10022                 this.mainFoot.show();
10023             }
10024         }
10025         
10026         Roo.each(this.el.select('tbody td', true).elements, function(e){
10027             e.on('mouseover', _this.onMouseover, _this);
10028         });
10029         
10030         Roo.each(this.el.select('tbody td', true).elements, function(e){
10031             e.on('mouseout', _this.onMouseout, _this);
10032         });
10033         this.fireEvent('rowsrendered', this);
10034         
10035         this.autoSize();
10036         
10037         this.initCSS(); /// resize cols
10038
10039         
10040     },
10041     
10042     
10043     onUpdate : function(ds,record)
10044     {
10045         this.refreshRow(record);
10046         this.autoSize();
10047     },
10048     
10049     onRemove : function(ds, record, index, isUpdate){
10050         if(isUpdate !== true){
10051             this.fireEvent("beforerowremoved", this, index, record);
10052         }
10053         var bt = this.bodyEl.dom;
10054         
10055         var rows = this.el.select('tbody > tr', true).elements;
10056         
10057         if(typeof(rows[index]) != 'undefined'){
10058             bt.removeChild(rows[index].dom);
10059         }
10060         
10061 //        if(bt.rows[index]){
10062 //            bt.removeChild(bt.rows[index]);
10063 //        }
10064         
10065         if(isUpdate !== true){
10066             //this.stripeRows(index);
10067             //this.syncRowHeights(index, index);
10068             //this.layout();
10069             this.fireEvent("rowremoved", this, index, record);
10070         }
10071     },
10072     
10073     onAdd : function(ds, records, rowIndex)
10074     {
10075         //Roo.log('on Add called');
10076         // - note this does not handle multiple adding very well..
10077         var bt = this.bodyEl.dom;
10078         for (var i =0 ; i < records.length;i++) {
10079             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10080             //Roo.log(records[i]);
10081             //Roo.log(this.store.getAt(rowIndex+i));
10082             this.insertRow(this.store, rowIndex + i, false);
10083             return;
10084         }
10085         
10086     },
10087     
10088     
10089     refreshRow : function(record){
10090         var ds = this.store, index;
10091         if(typeof record == 'number'){
10092             index = record;
10093             record = ds.getAt(index);
10094         }else{
10095             index = ds.indexOf(record);
10096             if (index < 0) {
10097                 return; // should not happen - but seems to 
10098             }
10099         }
10100         this.insertRow(ds, index, true);
10101         this.autoSize();
10102         this.onRemove(ds, record, index+1, true);
10103         this.autoSize();
10104         //this.syncRowHeights(index, index);
10105         //this.layout();
10106         this.fireEvent("rowupdated", this, index, record);
10107     },
10108     // private - called by RowSelection
10109     onRowSelect : function(rowIndex){
10110         var row = this.getRowDom(rowIndex);
10111         row.addClass(['bg-info','info']);
10112     },
10113     // private - called by RowSelection
10114     onRowDeselect : function(rowIndex)
10115     {
10116         if (rowIndex < 0) {
10117             return;
10118         }
10119         var row = this.getRowDom(rowIndex);
10120         row.removeClass(['bg-info','info']);
10121     },
10122       /**
10123      * Focuses the specified row.
10124      * @param {Number} row The row index
10125      */
10126     focusRow : function(row)
10127     {
10128         //Roo.log('GridView.focusRow');
10129         var x = this.bodyEl.dom.scrollLeft;
10130         this.focusCell(row, 0, false);
10131         this.bodyEl.dom.scrollLeft = x;
10132
10133     },
10134      /**
10135      * Focuses the specified cell.
10136      * @param {Number} row The row index
10137      * @param {Number} col The column index
10138      * @param {Boolean} hscroll false to disable horizontal scrolling
10139      */
10140     focusCell : function(row, col, hscroll)
10141     {
10142         //Roo.log('GridView.focusCell');
10143         var el = this.ensureVisible(row, col, hscroll);
10144         // not sure what focusEL achives = it's a <a> pos relative 
10145         //this.focusEl.alignTo(el, "tl-tl");
10146         //if(Roo.isGecko){
10147         //    this.focusEl.focus();
10148         //}else{
10149         //    this.focusEl.focus.defer(1, this.focusEl);
10150         //}
10151     },
10152     
10153      /**
10154      * Scrolls the specified cell into view
10155      * @param {Number} row The row index
10156      * @param {Number} col The column index
10157      * @param {Boolean} hscroll false to disable horizontal scrolling
10158      */
10159     ensureVisible : function(row, col, hscroll)
10160     {
10161         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10162         //return null; //disable for testing.
10163         if(typeof row != "number"){
10164             row = row.rowIndex;
10165         }
10166         if(row < 0 && row >= this.ds.getCount()){
10167             return  null;
10168         }
10169         col = (col !== undefined ? col : 0);
10170         var cm = this.cm;
10171         while(cm.isHidden(col)){
10172             col++;
10173         }
10174
10175         var el = this.getCellDom(row, col);
10176         if(!el){
10177             return null;
10178         }
10179         var c = this.bodyEl.dom;
10180
10181         var ctop = parseInt(el.offsetTop, 10);
10182         var cleft = parseInt(el.offsetLeft, 10);
10183         var cbot = ctop + el.offsetHeight;
10184         var cright = cleft + el.offsetWidth;
10185
10186         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10187         var ch = 0; //?? header is not withing the area?
10188         var stop = parseInt(c.scrollTop, 10);
10189         var sleft = parseInt(c.scrollLeft, 10);
10190         var sbot = stop + ch;
10191         var sright = sleft + c.clientWidth;
10192         /*
10193         Roo.log('GridView.ensureVisible:' +
10194                 ' ctop:' + ctop +
10195                 ' c.clientHeight:' + c.clientHeight +
10196                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10197                 ' stop:' + stop +
10198                 ' cbot:' + cbot +
10199                 ' sbot:' + sbot +
10200                 ' ch:' + ch  
10201                 );
10202         */
10203         if(ctop < stop){
10204             c.scrollTop = ctop;
10205             //Roo.log("set scrolltop to ctop DISABLE?");
10206         }else if(cbot > sbot){
10207             //Roo.log("set scrolltop to cbot-ch");
10208             c.scrollTop = cbot-ch;
10209         }
10210
10211         if(hscroll !== false){
10212             if(cleft < sleft){
10213                 c.scrollLeft = cleft;
10214             }else if(cright > sright){
10215                 c.scrollLeft = cright-c.clientWidth;
10216             }
10217         }
10218
10219         return el;
10220     },
10221     
10222     
10223     insertRow : function(dm, rowIndex, isUpdate){
10224         
10225         if(!isUpdate){
10226             this.fireEvent("beforerowsinserted", this, rowIndex);
10227         }
10228             //var s = this.getScrollState();
10229         var row = this.renderRow(this.cm, this.store, rowIndex);
10230         // insert before rowIndex..
10231         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10232         
10233         var _this = this;
10234                 
10235         if(row.cellObjects.length){
10236             Roo.each(row.cellObjects, function(r){
10237                 _this.renderCellObject(r);
10238             })
10239         }
10240             
10241         if(!isUpdate){
10242             this.fireEvent("rowsinserted", this, rowIndex);
10243             //this.syncRowHeights(firstRow, lastRow);
10244             //this.stripeRows(firstRow);
10245             //this.layout();
10246         }
10247         
10248     },
10249     
10250     
10251     getRowDom : function(rowIndex)
10252     {
10253         var rows = this.el.select('tbody > tr', true).elements;
10254         
10255         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10256         
10257     },
10258     getCellDom : function(rowIndex, colIndex)
10259     {
10260         var row = this.getRowDom(rowIndex);
10261         if (row === false) {
10262             return false;
10263         }
10264         var cols = row.select('td', true).elements;
10265         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10266         
10267     },
10268     
10269     // returns the object tree for a tr..
10270   
10271     
10272     renderRow : function(cm, ds, rowIndex) 
10273     {
10274         var d = ds.getAt(rowIndex);
10275         
10276         var row = {
10277             tag : 'tr',
10278             cls : 'x-row-' + rowIndex,
10279             cn : []
10280         };
10281             
10282         var cellObjects = [];
10283         
10284         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10285             var config = cm.config[i];
10286             
10287             var renderer = cm.getRenderer(i);
10288             var value = '';
10289             var id = false;
10290             
10291             if(typeof(renderer) !== 'undefined'){
10292                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10293             }
10294             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10295             // and are rendered into the cells after the row is rendered - using the id for the element.
10296             
10297             if(typeof(value) === 'object'){
10298                 id = Roo.id();
10299                 cellObjects.push({
10300                     container : id,
10301                     cfg : value 
10302                 })
10303             }
10304             
10305             var rowcfg = {
10306                 record: d,
10307                 rowIndex : rowIndex,
10308                 colIndex : i,
10309                 rowClass : ''
10310             };
10311
10312             this.fireEvent('rowclass', this, rowcfg);
10313             
10314             var td = {
10315                 tag: 'td',
10316                 // this might end up displaying HTML?
10317                 // this is too messy... - better to only do it on columsn you know are going to be too long
10318                 //tooltip : (typeof(value) === 'object') ? '' : value,
10319                 cls : rowcfg.rowClass + ' x-col-' + i,
10320                 style: '',
10321                 html: (typeof(value) === 'object') ? '' : value
10322             };
10323             
10324             if (id) {
10325                 td.id = id;
10326             }
10327             
10328             if(typeof(config.colspan) != 'undefined'){
10329                 td.colspan = config.colspan;
10330             }
10331             
10332             
10333             
10334             if(typeof(config.align) != 'undefined' && config.align.length){
10335                 td.style += ' text-align:' + config.align + ';';
10336             }
10337             if(typeof(config.valign) != 'undefined' && config.valign.length){
10338                 td.style += ' vertical-align:' + config.valign + ';';
10339             }
10340             /*
10341             if(typeof(config.width) != 'undefined'){
10342                 td.style += ' width:' +  config.width + 'px;';
10343             }
10344             */
10345             
10346             if(typeof(config.cursor) != 'undefined'){
10347                 td.style += ' cursor:' +  config.cursor + ';';
10348             }
10349             
10350             if(typeof(config.cls) != 'undefined'){
10351                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10352             }
10353             if (this.responsive) {
10354                 ['xs','sm','md','lg'].map(function(size){
10355                     
10356                     if(typeof(config[size]) == 'undefined'){
10357                         return;
10358                     }
10359                     
10360                     
10361                       
10362                     if (!config[size]) { // 0 = hidden
10363                         // BS 4 '0' is treated as hide that column and below.
10364                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10365                         return;
10366                     }
10367                     
10368                     td.cls += ' col-' + size + '-' + config[size] + (
10369                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10370                     );
10371                      
10372     
10373                 });
10374             }
10375             row.cn.push(td);
10376            
10377         }
10378         
10379         row.cellObjects = cellObjects;
10380         
10381         return row;
10382           
10383     },
10384     
10385     
10386     
10387     onBeforeLoad : function()
10388     {
10389         this.el.unmask(); // if needed.
10390     },
10391      /**
10392      * Remove all rows
10393      */
10394     clear : function()
10395     {
10396         this.el.select('tbody', true).first().dom.innerHTML = '';
10397     },
10398     /**
10399      * Show or hide a row.
10400      * @param {Number} rowIndex to show or hide
10401      * @param {Boolean} state hide
10402      */
10403     setRowVisibility : function(rowIndex, state)
10404     {
10405         var bt = this.bodyEl.dom;
10406         
10407         var rows = this.el.select('tbody > tr', true).elements;
10408         
10409         if(typeof(rows[rowIndex]) == 'undefined'){
10410             return;
10411         }
10412         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10413         
10414     },
10415     
10416     
10417     getSelectionModel : function(){
10418         if(!this.selModel){
10419             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10420         }
10421         return this.selModel;
10422     },
10423     /*
10424      * Render the Roo.bootstrap object from renderder
10425      */
10426     renderCellObject : function(r)
10427     {
10428         var _this = this;
10429         
10430         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10431         
10432         var t = r.cfg.render(r.container);
10433         
10434         if(r.cfg.cn){
10435             Roo.each(r.cfg.cn, function(c){
10436                 var child = {
10437                     container: t.getChildContainer(),
10438                     cfg: c
10439                 };
10440                 _this.renderCellObject(child);
10441             })
10442         }
10443     },
10444     /**
10445      * get the Row Index from a dom element.
10446      * @param {Roo.Element} row The row to look for
10447      * @returns {Number} the row
10448      */
10449     getRowIndex : function(row)
10450     {
10451         var rowIndex = -1;
10452         
10453         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10454             if(el != row){
10455                 return;
10456             }
10457             
10458             rowIndex = index;
10459         });
10460         
10461         return rowIndex;
10462     },
10463     /**
10464      * get the header TH element for columnIndex
10465      * @param {Number} columnIndex
10466      * @returns {Roo.Element}
10467      */
10468     getHeaderIndex: function(colIndex)
10469     {
10470         var cols = this.headEl.select('th', true).elements;
10471         return cols[colIndex]; 
10472     },
10473     /**
10474      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10475      * @param {domElement} cell to look for
10476      * @returns {Number} the column
10477      */
10478     getCellIndex : function(cell)
10479     {
10480         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10481         if(id){
10482             return parseInt(id[1], 10);
10483         }
10484         return 0;
10485     },
10486      /**
10487      * Returns the grid's underlying element = used by panel.Grid
10488      * @return {Element} The element
10489      */
10490     getGridEl : function(){
10491         return this.el;
10492     },
10493      /**
10494      * Forces a resize - used by panel.Grid
10495      * @return {Element} The element
10496      */
10497     autoSize : function()
10498     {
10499         //var ctr = Roo.get(this.container.dom.parentElement);
10500         var ctr = Roo.get(this.el.dom);
10501         
10502         var thd = this.getGridEl().select('thead',true).first();
10503         var tbd = this.getGridEl().select('tbody', true).first();
10504         var tfd = this.getGridEl().select('tfoot', true).first();
10505         
10506         var cw = ctr.getWidth();
10507         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10508         
10509         if (tbd) {
10510             
10511             tbd.setWidth(ctr.getWidth());
10512             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10513             // this needs fixing for various usage - currently only hydra job advers I think..
10514             //tdb.setHeight(
10515             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10516             //); 
10517             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10518             cw -= barsize;
10519         }
10520         cw = Math.max(cw, this.totalWidth);
10521         this.getGridEl().select('tbody tr',true).setWidth(cw);
10522         this.initCSS();
10523         
10524         // resize 'expandable coloumn?
10525         
10526         return; // we doe not have a view in this design..
10527         
10528     },
10529     onBodyScroll: function()
10530     {
10531         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10532         if(this.headEl){
10533             this.headEl.setStyle({
10534                 'position' : 'relative',
10535                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10536             });
10537         }
10538         
10539         if(this.lazyLoad){
10540             
10541             var scrollHeight = this.bodyEl.dom.scrollHeight;
10542             
10543             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10544             
10545             var height = this.bodyEl.getHeight();
10546             
10547             if(scrollHeight - height == scrollTop) {
10548                 
10549                 var total = this.ds.getTotalCount();
10550                 
10551                 if(this.footer.cursor + this.footer.pageSize < total){
10552                     
10553                     this.footer.ds.load({
10554                         params : {
10555                             start : this.footer.cursor + this.footer.pageSize,
10556                             limit : this.footer.pageSize
10557                         },
10558                         add : true
10559                     });
10560                 }
10561             }
10562             
10563         }
10564     },
10565     onColumnSplitterMoved : function(i, diff)
10566     {
10567         this.userResized = true;
10568         
10569         var cm = this.colModel;
10570         
10571         var w = this.getHeaderIndex(i).getWidth() + diff;
10572         
10573         
10574         cm.setColumnWidth(i, w, true);
10575         this.initCSS();
10576         //var cid = cm.getColumnId(i); << not used in this version?
10577        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10578         
10579         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10580         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10581         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10582 */
10583         //this.updateSplitters();
10584         //this.layout(); << ??
10585         this.fireEvent("columnresize", i, w);
10586     },
10587     onHeaderChange : function()
10588     {
10589         var header = this.renderHeader();
10590         var table = this.el.select('table', true).first();
10591         
10592         this.headEl.remove();
10593         this.headEl = table.createChild(header, this.bodyEl, false);
10594         
10595         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10596             e.on('click', this.sort, this);
10597         }, this);
10598         
10599         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10600             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10601         }
10602         
10603     },
10604     
10605     onHiddenChange : function(colModel, colIndex, hidden)
10606     {
10607         /*
10608         this.cm.setHidden()
10609         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10610         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10611         
10612         this.CSS.updateRule(thSelector, "display", "");
10613         this.CSS.updateRule(tdSelector, "display", "");
10614         
10615         if(hidden){
10616             this.CSS.updateRule(thSelector, "display", "none");
10617             this.CSS.updateRule(tdSelector, "display", "none");
10618         }
10619         */
10620         // onload calls initCSS()
10621         this.onHeaderChange();
10622         this.onLoad();
10623     },
10624     
10625     setColumnWidth: function(col_index, width)
10626     {
10627         // width = "md-2 xs-2..."
10628         if(!this.colModel.config[col_index]) {
10629             return;
10630         }
10631         
10632         var w = width.split(" ");
10633         
10634         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10635         
10636         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10637         
10638         
10639         for(var j = 0; j < w.length; j++) {
10640             
10641             if(!w[j]) {
10642                 continue;
10643             }
10644             
10645             var size_cls = w[j].split("-");
10646             
10647             if(!Number.isInteger(size_cls[1] * 1)) {
10648                 continue;
10649             }
10650             
10651             if(!this.colModel.config[col_index][size_cls[0]]) {
10652                 continue;
10653             }
10654             
10655             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10656                 continue;
10657             }
10658             
10659             h_row[0].classList.replace(
10660                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10661                 "col-"+size_cls[0]+"-"+size_cls[1]
10662             );
10663             
10664             for(var i = 0; i < rows.length; i++) {
10665                 
10666                 var size_cls = w[j].split("-");
10667                 
10668                 if(!Number.isInteger(size_cls[1] * 1)) {
10669                     continue;
10670                 }
10671                 
10672                 if(!this.colModel.config[col_index][size_cls[0]]) {
10673                     continue;
10674                 }
10675                 
10676                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10677                     continue;
10678                 }
10679                 
10680                 rows[i].classList.replace(
10681                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10682                     "col-"+size_cls[0]+"-"+size_cls[1]
10683                 );
10684             }
10685             
10686             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10687         }
10688     }
10689 });
10690
10691 // currently only used to find the split on drag.. 
10692 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10693
10694 /**
10695  * @depricated
10696 */
10697 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10698 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10699 /*
10700  * - LGPL
10701  *
10702  * table cell
10703  * 
10704  */
10705
10706 /**
10707  * @class Roo.bootstrap.TableCell
10708  * @extends Roo.bootstrap.Component
10709  * @children Roo.bootstrap.Component
10710  * @parent Roo.bootstrap.TableRow
10711  * Bootstrap TableCell class
10712  * 
10713  * @cfg {String} html cell contain text
10714  * @cfg {String} cls cell class
10715  * @cfg {String} tag cell tag (td|th) default td
10716  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10717  * @cfg {String} align Aligns the content in a cell
10718  * @cfg {String} axis Categorizes cells
10719  * @cfg {String} bgcolor Specifies the background color of a cell
10720  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10721  * @cfg {Number} colspan Specifies the number of columns a cell should span
10722  * @cfg {String} headers Specifies one or more header cells a cell is related to
10723  * @cfg {Number} height Sets the height of a cell
10724  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10725  * @cfg {Number} rowspan Sets the number of rows a cell should span
10726  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10727  * @cfg {String} valign Vertical aligns the content in a cell
10728  * @cfg {Number} width Specifies the width of a cell
10729  * 
10730  * @constructor
10731  * Create a new TableCell
10732  * @param {Object} config The config object
10733  */
10734
10735 Roo.bootstrap.TableCell = function(config){
10736     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10737 };
10738
10739 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10740     
10741     html: false,
10742     cls: false,
10743     tag: false,
10744     abbr: false,
10745     align: false,
10746     axis: false,
10747     bgcolor: false,
10748     charoff: false,
10749     colspan: false,
10750     headers: false,
10751     height: false,
10752     nowrap: false,
10753     rowspan: false,
10754     scope: false,
10755     valign: false,
10756     width: false,
10757     
10758     
10759     getAutoCreate : function(){
10760         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10761         
10762         cfg = {
10763             tag: 'td'
10764         };
10765         
10766         if(this.tag){
10767             cfg.tag = this.tag;
10768         }
10769         
10770         if (this.html) {
10771             cfg.html=this.html
10772         }
10773         if (this.cls) {
10774             cfg.cls=this.cls
10775         }
10776         if (this.abbr) {
10777             cfg.abbr=this.abbr
10778         }
10779         if (this.align) {
10780             cfg.align=this.align
10781         }
10782         if (this.axis) {
10783             cfg.axis=this.axis
10784         }
10785         if (this.bgcolor) {
10786             cfg.bgcolor=this.bgcolor
10787         }
10788         if (this.charoff) {
10789             cfg.charoff=this.charoff
10790         }
10791         if (this.colspan) {
10792             cfg.colspan=this.colspan
10793         }
10794         if (this.headers) {
10795             cfg.headers=this.headers
10796         }
10797         if (this.height) {
10798             cfg.height=this.height
10799         }
10800         if (this.nowrap) {
10801             cfg.nowrap=this.nowrap
10802         }
10803         if (this.rowspan) {
10804             cfg.rowspan=this.rowspan
10805         }
10806         if (this.scope) {
10807             cfg.scope=this.scope
10808         }
10809         if (this.valign) {
10810             cfg.valign=this.valign
10811         }
10812         if (this.width) {
10813             cfg.width=this.width
10814         }
10815         
10816         
10817         return cfg;
10818     }
10819    
10820 });
10821
10822  
10823
10824  /*
10825  * - LGPL
10826  *
10827  * table row
10828  * 
10829  */
10830
10831 /**
10832  * @class Roo.bootstrap.TableRow
10833  * @extends Roo.bootstrap.Component
10834  * @children Roo.bootstrap.TableCell
10835  * @parent Roo.bootstrap.TableBody
10836  * Bootstrap TableRow class
10837  * @cfg {String} cls row class
10838  * @cfg {String} align Aligns the content in a table row
10839  * @cfg {String} bgcolor Specifies a background color for a table row
10840  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10841  * @cfg {String} valign Vertical aligns the content in a table row
10842  * 
10843  * @constructor
10844  * Create a new TableRow
10845  * @param {Object} config The config object
10846  */
10847
10848 Roo.bootstrap.TableRow = function(config){
10849     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10850 };
10851
10852 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10853     
10854     cls: false,
10855     align: false,
10856     bgcolor: false,
10857     charoff: false,
10858     valign: false,
10859     
10860     getAutoCreate : function(){
10861         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10862         
10863         cfg = {
10864             tag: 'tr'
10865         };
10866             
10867         if(this.cls){
10868             cfg.cls = this.cls;
10869         }
10870         if(this.align){
10871             cfg.align = this.align;
10872         }
10873         if(this.bgcolor){
10874             cfg.bgcolor = this.bgcolor;
10875         }
10876         if(this.charoff){
10877             cfg.charoff = this.charoff;
10878         }
10879         if(this.valign){
10880             cfg.valign = this.valign;
10881         }
10882         
10883         return cfg;
10884     }
10885    
10886 });
10887
10888  
10889
10890  /*
10891  * - LGPL
10892  *
10893  * table body
10894  * 
10895  */
10896
10897 /**
10898  * @class Roo.bootstrap.TableBody
10899  * @extends Roo.bootstrap.Component
10900  * @children Roo.bootstrap.TableRow
10901  * @parent Roo.bootstrap.Table
10902  * Bootstrap TableBody class
10903  * @cfg {String} cls element class
10904  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10905  * @cfg {String} align Aligns the content inside the element
10906  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10907  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10908  * 
10909  * @constructor
10910  * Create a new TableBody
10911  * @param {Object} config The config object
10912  */
10913
10914 Roo.bootstrap.TableBody = function(config){
10915     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10916 };
10917
10918 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10919     
10920     cls: false,
10921     tag: false,
10922     align: false,
10923     charoff: false,
10924     valign: false,
10925     
10926     getAutoCreate : function(){
10927         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10928         
10929         cfg = {
10930             tag: 'tbody'
10931         };
10932             
10933         if (this.cls) {
10934             cfg.cls=this.cls
10935         }
10936         if(this.tag){
10937             cfg.tag = this.tag;
10938         }
10939         
10940         if(this.align){
10941             cfg.align = this.align;
10942         }
10943         if(this.charoff){
10944             cfg.charoff = this.charoff;
10945         }
10946         if(this.valign){
10947             cfg.valign = this.valign;
10948         }
10949         
10950         return cfg;
10951     }
10952     
10953     
10954 //    initEvents : function()
10955 //    {
10956 //        
10957 //        if(!this.store){
10958 //            return;
10959 //        }
10960 //        
10961 //        this.store = Roo.factory(this.store, Roo.data);
10962 //        this.store.on('load', this.onLoad, this);
10963 //        
10964 //        this.store.load();
10965 //        
10966 //    },
10967 //    
10968 //    onLoad: function () 
10969 //    {   
10970 //        this.fireEvent('load', this);
10971 //    }
10972 //    
10973 //   
10974 });
10975
10976  
10977
10978  /*
10979  * Based on:
10980  * Ext JS Library 1.1.1
10981  * Copyright(c) 2006-2007, Ext JS, LLC.
10982  *
10983  * Originally Released Under LGPL - original licence link has changed is not relivant.
10984  *
10985  * Fork - LGPL
10986  * <script type="text/javascript">
10987  */
10988
10989 // as we use this in bootstrap.
10990 Roo.namespace('Roo.form');
10991  /**
10992  * @class Roo.form.Action
10993  * Internal Class used to handle form actions
10994  * @constructor
10995  * @param {Roo.form.BasicForm} el The form element or its id
10996  * @param {Object} config Configuration options
10997  */
10998
10999  
11000  
11001 // define the action interface
11002 Roo.form.Action = function(form, options){
11003     this.form = form;
11004     this.options = options || {};
11005 };
11006 /**
11007  * Client Validation Failed
11008  * @const 
11009  */
11010 Roo.form.Action.CLIENT_INVALID = 'client';
11011 /**
11012  * Server Validation Failed
11013  * @const 
11014  */
11015 Roo.form.Action.SERVER_INVALID = 'server';
11016  /**
11017  * Connect to Server Failed
11018  * @const 
11019  */
11020 Roo.form.Action.CONNECT_FAILURE = 'connect';
11021 /**
11022  * Reading Data from Server Failed
11023  * @const 
11024  */
11025 Roo.form.Action.LOAD_FAILURE = 'load';
11026
11027 Roo.form.Action.prototype = {
11028     type : 'default',
11029     failureType : undefined,
11030     response : undefined,
11031     result : undefined,
11032
11033     // interface method
11034     run : function(options){
11035
11036     },
11037
11038     // interface method
11039     success : function(response){
11040
11041     },
11042
11043     // interface method
11044     handleResponse : function(response){
11045
11046     },
11047
11048     // default connection failure
11049     failure : function(response){
11050         
11051         this.response = response;
11052         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11053         this.form.afterAction(this, false);
11054     },
11055
11056     processResponse : function(response){
11057         this.response = response;
11058         if(!response.responseText){
11059             return true;
11060         }
11061         this.result = this.handleResponse(response);
11062         return this.result;
11063     },
11064
11065     // utility functions used internally
11066     getUrl : function(appendParams){
11067         var url = this.options.url || this.form.url || this.form.el.dom.action;
11068         if(appendParams){
11069             var p = this.getParams();
11070             if(p){
11071                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11072             }
11073         }
11074         return url;
11075     },
11076
11077     getMethod : function(){
11078         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11079     },
11080
11081     getParams : function(){
11082         var bp = this.form.baseParams;
11083         var p = this.options.params;
11084         if(p){
11085             if(typeof p == "object"){
11086                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11087             }else if(typeof p == 'string' && bp){
11088                 p += '&' + Roo.urlEncode(bp);
11089             }
11090         }else if(bp){
11091             p = Roo.urlEncode(bp);
11092         }
11093         return p;
11094     },
11095
11096     createCallback : function(){
11097         return {
11098             success: this.success,
11099             failure: this.failure,
11100             scope: this,
11101             timeout: (this.form.timeout*1000),
11102             upload: this.form.fileUpload ? this.success : undefined
11103         };
11104     }
11105 };
11106
11107 Roo.form.Action.Submit = function(form, options){
11108     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11109 };
11110
11111 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11112     type : 'submit',
11113
11114     haveProgress : false,
11115     uploadComplete : false,
11116     
11117     // uploadProgress indicator.
11118     uploadProgress : function()
11119     {
11120         if (!this.form.progressUrl) {
11121             return;
11122         }
11123         
11124         if (!this.haveProgress) {
11125             Roo.MessageBox.progress("Uploading", "Uploading");
11126         }
11127         if (this.uploadComplete) {
11128            Roo.MessageBox.hide();
11129            return;
11130         }
11131         
11132         this.haveProgress = true;
11133    
11134         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11135         
11136         var c = new Roo.data.Connection();
11137         c.request({
11138             url : this.form.progressUrl,
11139             params: {
11140                 id : uid
11141             },
11142             method: 'GET',
11143             success : function(req){
11144                //console.log(data);
11145                 var rdata = false;
11146                 var edata;
11147                 try  {
11148                    rdata = Roo.decode(req.responseText)
11149                 } catch (e) {
11150                     Roo.log("Invalid data from server..");
11151                     Roo.log(edata);
11152                     return;
11153                 }
11154                 if (!rdata || !rdata.success) {
11155                     Roo.log(rdata);
11156                     Roo.MessageBox.alert(Roo.encode(rdata));
11157                     return;
11158                 }
11159                 var data = rdata.data;
11160                 
11161                 if (this.uploadComplete) {
11162                    Roo.MessageBox.hide();
11163                    return;
11164                 }
11165                    
11166                 if (data){
11167                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11168                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11169                     );
11170                 }
11171                 this.uploadProgress.defer(2000,this);
11172             },
11173        
11174             failure: function(data) {
11175                 Roo.log('progress url failed ');
11176                 Roo.log(data);
11177             },
11178             scope : this
11179         });
11180            
11181     },
11182     
11183     
11184     run : function()
11185     {
11186         // run get Values on the form, so it syncs any secondary forms.
11187         this.form.getValues();
11188         
11189         var o = this.options;
11190         var method = this.getMethod();
11191         var isPost = method == 'POST';
11192         if(o.clientValidation === false || this.form.isValid()){
11193             
11194             if (this.form.progressUrl) {
11195                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11196                     (new Date() * 1) + '' + Math.random());
11197                     
11198             } 
11199             
11200             
11201             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11202                 form:this.form.el.dom,
11203                 url:this.getUrl(!isPost),
11204                 method: method,
11205                 params:isPost ? this.getParams() : null,
11206                 isUpload: this.form.fileUpload,
11207                 formData : this.form.formData
11208             }));
11209             
11210             this.uploadProgress();
11211
11212         }else if (o.clientValidation !== false){ // client validation failed
11213             this.failureType = Roo.form.Action.CLIENT_INVALID;
11214             this.form.afterAction(this, false);
11215         }
11216     },
11217
11218     success : function(response)
11219     {
11220         this.uploadComplete= true;
11221         if (this.haveProgress) {
11222             Roo.MessageBox.hide();
11223         }
11224         
11225         
11226         var result = this.processResponse(response);
11227         if(result === true || result.success){
11228             this.form.afterAction(this, true);
11229             return;
11230         }
11231         if(result.errors){
11232             this.form.markInvalid(result.errors);
11233             this.failureType = Roo.form.Action.SERVER_INVALID;
11234         }
11235         this.form.afterAction(this, false);
11236     },
11237     failure : function(response)
11238     {
11239         this.uploadComplete= true;
11240         if (this.haveProgress) {
11241             Roo.MessageBox.hide();
11242         }
11243         
11244         this.response = response;
11245         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11246         this.form.afterAction(this, false);
11247     },
11248     
11249     handleResponse : function(response){
11250         if(this.form.errorReader){
11251             var rs = this.form.errorReader.read(response);
11252             var errors = [];
11253             if(rs.records){
11254                 for(var i = 0, len = rs.records.length; i < len; i++) {
11255                     var r = rs.records[i];
11256                     errors[i] = r.data;
11257                 }
11258             }
11259             if(errors.length < 1){
11260                 errors = null;
11261             }
11262             return {
11263                 success : rs.success,
11264                 errors : errors
11265             };
11266         }
11267         var ret = false;
11268         try {
11269             ret = Roo.decode(response.responseText);
11270         } catch (e) {
11271             ret = {
11272                 success: false,
11273                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11274                 errors : []
11275             };
11276         }
11277         return ret;
11278         
11279     }
11280 });
11281
11282
11283 Roo.form.Action.Load = function(form, options){
11284     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11285     this.reader = this.form.reader;
11286 };
11287
11288 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11289     type : 'load',
11290
11291     run : function(){
11292         
11293         Roo.Ajax.request(Roo.apply(
11294                 this.createCallback(), {
11295                     method:this.getMethod(),
11296                     url:this.getUrl(false),
11297                     params:this.getParams()
11298         }));
11299     },
11300
11301     success : function(response){
11302         
11303         var result = this.processResponse(response);
11304         if(result === true || !result.success || !result.data){
11305             this.failureType = Roo.form.Action.LOAD_FAILURE;
11306             this.form.afterAction(this, false);
11307             return;
11308         }
11309         this.form.clearInvalid();
11310         this.form.setValues(result.data);
11311         this.form.afterAction(this, true);
11312     },
11313
11314     handleResponse : function(response){
11315         if(this.form.reader){
11316             var rs = this.form.reader.read(response);
11317             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11318             return {
11319                 success : rs.success,
11320                 data : data
11321             };
11322         }
11323         return Roo.decode(response.responseText);
11324     }
11325 });
11326
11327 Roo.form.Action.ACTION_TYPES = {
11328     'load' : Roo.form.Action.Load,
11329     'submit' : Roo.form.Action.Submit
11330 };/*
11331  * - LGPL
11332  *
11333  * form
11334  *
11335  */
11336
11337 /**
11338  * @class Roo.bootstrap.form.Form
11339  * @extends Roo.bootstrap.Component
11340  * @children Roo.bootstrap.Component
11341  * Bootstrap Form class
11342  * @cfg {String} method  GET | POST (default POST)
11343  * @cfg {String} labelAlign top | left (default top)
11344  * @cfg {String} align left  | right - for navbars
11345  * @cfg {Boolean} loadMask load mask when submit (default true)
11346
11347  *
11348  * @constructor
11349  * Create a new Form
11350  * @param {Object} config The config object
11351  */
11352
11353
11354 Roo.bootstrap.form.Form = function(config){
11355     
11356     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11357     
11358     Roo.bootstrap.form.Form.popover.apply();
11359     
11360     this.addEvents({
11361         /**
11362          * @event clientvalidation
11363          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11364          * @param {Form} this
11365          * @param {Boolean} valid true if the form has passed client-side validation
11366          */
11367         clientvalidation: true,
11368         /**
11369          * @event beforeaction
11370          * Fires before any action is performed. Return false to cancel the action.
11371          * @param {Form} this
11372          * @param {Action} action The action to be performed
11373          */
11374         beforeaction: true,
11375         /**
11376          * @event actionfailed
11377          * Fires when an action fails.
11378          * @param {Form} this
11379          * @param {Action} action The action that failed
11380          */
11381         actionfailed : true,
11382         /**
11383          * @event actioncomplete
11384          * Fires when an action is completed.
11385          * @param {Form} this
11386          * @param {Action} action The action that completed
11387          */
11388         actioncomplete : true
11389     });
11390 };
11391
11392 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11393
11394      /**
11395      * @cfg {String} method
11396      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11397      */
11398     method : 'POST',
11399     /**
11400      * @cfg {String} url
11401      * The URL to use for form actions if one isn't supplied in the action options.
11402      */
11403     /**
11404      * @cfg {Boolean} fileUpload
11405      * Set to true if this form is a file upload.
11406      */
11407
11408     /**
11409      * @cfg {Object} baseParams
11410      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11411      */
11412
11413     /**
11414      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11415      */
11416     timeout: 30,
11417     /**
11418      * @cfg {Sting} align (left|right) for navbar forms
11419      */
11420     align : 'left',
11421
11422     // private
11423     activeAction : null,
11424
11425     /**
11426      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11427      * element by passing it or its id or mask the form itself by passing in true.
11428      * @type Mixed
11429      */
11430     waitMsgTarget : false,
11431
11432     loadMask : true,
11433     
11434     /**
11435      * @cfg {Boolean} errorMask (true|false) default false
11436      */
11437     errorMask : false,
11438     
11439     /**
11440      * @cfg {Number} maskOffset Default 100
11441      */
11442     maskOffset : 100,
11443     
11444     /**
11445      * @cfg {Boolean} maskBody
11446      */
11447     maskBody : false,
11448
11449     getAutoCreate : function(){
11450
11451         var cfg = {
11452             tag: 'form',
11453             method : this.method || 'POST',
11454             id : this.id || Roo.id(),
11455             cls : ''
11456         };
11457         if (this.parent().xtype.match(/^Nav/)) {
11458             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11459
11460         }
11461
11462         if (this.labelAlign == 'left' ) {
11463             cfg.cls += ' form-horizontal';
11464         }
11465
11466
11467         return cfg;
11468     },
11469     initEvents : function()
11470     {
11471         this.el.on('submit', this.onSubmit, this);
11472         // this was added as random key presses on the form where triggering form submit.
11473         this.el.on('keypress', function(e) {
11474             if (e.getCharCode() != 13) {
11475                 return true;
11476             }
11477             // we might need to allow it for textareas.. and some other items.
11478             // check e.getTarget().
11479
11480             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11481                 return true;
11482             }
11483
11484             Roo.log("keypress blocked");
11485
11486             e.preventDefault();
11487             return false;
11488         });
11489         
11490     },
11491     // private
11492     onSubmit : function(e){
11493         e.stopEvent();
11494     },
11495
11496      /**
11497      * Returns true if client-side validation on the form is successful.
11498      * @return Boolean
11499      */
11500     isValid : function(){
11501         var items = this.getItems();
11502         var valid = true;
11503         var target = false;
11504         
11505         items.each(function(f){
11506             
11507             if(f.validate()){
11508                 return;
11509             }
11510             
11511             Roo.log('invalid field: ' + f.name);
11512             
11513             valid = false;
11514
11515             if(!target && f.el.isVisible(true)){
11516                 target = f;
11517             }
11518            
11519         });
11520         
11521         if(this.errorMask && !valid){
11522             Roo.bootstrap.form.Form.popover.mask(this, target);
11523         }
11524         
11525         return valid;
11526     },
11527     
11528     /**
11529      * Returns true if any fields in this form have changed since their original load.
11530      * @return Boolean
11531      */
11532     isDirty : function(){
11533         var dirty = false;
11534         var items = this.getItems();
11535         items.each(function(f){
11536            if(f.isDirty()){
11537                dirty = true;
11538                return false;
11539            }
11540            return true;
11541         });
11542         return dirty;
11543     },
11544      /**
11545      * Performs a predefined action (submit or load) or custom actions you define on this form.
11546      * @param {String} actionName The name of the action type
11547      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11548      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11549      * accept other config options):
11550      * <pre>
11551 Property          Type             Description
11552 ----------------  ---------------  ----------------------------------------------------------------------------------
11553 url               String           The url for the action (defaults to the form's url)
11554 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11555 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11556 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11557                                    validate the form on the client (defaults to false)
11558      * </pre>
11559      * @return {BasicForm} this
11560      */
11561     doAction : function(action, options){
11562         if(typeof action == 'string'){
11563             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11564         }
11565         if(this.fireEvent('beforeaction', this, action) !== false){
11566             this.beforeAction(action);
11567             action.run.defer(100, action);
11568         }
11569         return this;
11570     },
11571
11572     // private
11573     beforeAction : function(action){
11574         var o = action.options;
11575         
11576         if(this.loadMask){
11577             
11578             if(this.maskBody){
11579                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11580             } else {
11581                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11582             }
11583         }
11584         // not really supported yet.. ??
11585
11586         //if(this.waitMsgTarget === true){
11587         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11588         //}else if(this.waitMsgTarget){
11589         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11590         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11591         //}else {
11592         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11593        // }
11594
11595     },
11596
11597     // private
11598     afterAction : function(action, success){
11599         this.activeAction = null;
11600         var o = action.options;
11601
11602         if(this.loadMask){
11603             
11604             if(this.maskBody){
11605                 Roo.get(document.body).unmask();
11606             } else {
11607                 this.el.unmask();
11608             }
11609         }
11610         
11611         //if(this.waitMsgTarget === true){
11612 //            this.el.unmask();
11613         //}else if(this.waitMsgTarget){
11614         //    this.waitMsgTarget.unmask();
11615         //}else{
11616         //    Roo.MessageBox.updateProgress(1);
11617         //    Roo.MessageBox.hide();
11618        // }
11619         //
11620         if(success){
11621             if(o.reset){
11622                 this.reset();
11623             }
11624             Roo.callback(o.success, o.scope, [this, action]);
11625             this.fireEvent('actioncomplete', this, action);
11626
11627         }else{
11628
11629             // failure condition..
11630             // we have a scenario where updates need confirming.
11631             // eg. if a locking scenario exists..
11632             // we look for { errors : { needs_confirm : true }} in the response.
11633             if (
11634                 (typeof(action.result) != 'undefined')  &&
11635                 (typeof(action.result.errors) != 'undefined')  &&
11636                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11637            ){
11638                 var _t = this;
11639                 Roo.log("not supported yet");
11640                  /*
11641
11642                 Roo.MessageBox.confirm(
11643                     "Change requires confirmation",
11644                     action.result.errorMsg,
11645                     function(r) {
11646                         if (r != 'yes') {
11647                             return;
11648                         }
11649                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11650                     }
11651
11652                 );
11653                 */
11654
11655
11656                 return;
11657             }
11658
11659             Roo.callback(o.failure, o.scope, [this, action]);
11660             // show an error message if no failed handler is set..
11661             if (!this.hasListener('actionfailed')) {
11662                 Roo.log("need to add dialog support");
11663                 /*
11664                 Roo.MessageBox.alert("Error",
11665                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11666                         action.result.errorMsg :
11667                         "Saving Failed, please check your entries or try again"
11668                 );
11669                 */
11670             }
11671
11672             this.fireEvent('actionfailed', this, action);
11673         }
11674
11675     },
11676     /**
11677      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11678      * @param {String} id The value to search for
11679      * @return Field
11680      */
11681     findField : function(id){
11682         var items = this.getItems();
11683         var field = items.get(id);
11684         if(!field){
11685              items.each(function(f){
11686                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11687                     field = f;
11688                     return false;
11689                 }
11690                 return true;
11691             });
11692         }
11693         return field || null;
11694     },
11695      /**
11696      * Mark fields in this form invalid in bulk.
11697      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11698      * @return {BasicForm} this
11699      */
11700     markInvalid : function(errors){
11701         if(errors instanceof Array){
11702             for(var i = 0, len = errors.length; i < len; i++){
11703                 var fieldError = errors[i];
11704                 var f = this.findField(fieldError.id);
11705                 if(f){
11706                     f.markInvalid(fieldError.msg);
11707                 }
11708             }
11709         }else{
11710             var field, id;
11711             for(id in errors){
11712                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11713                     field.markInvalid(errors[id]);
11714                 }
11715             }
11716         }
11717         //Roo.each(this.childForms || [], function (f) {
11718         //    f.markInvalid(errors);
11719         //});
11720
11721         return this;
11722     },
11723
11724     /**
11725      * Set values for fields in this form in bulk.
11726      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11727      * @return {BasicForm} this
11728      */
11729     setValues : function(values){
11730         if(values instanceof Array){ // array of objects
11731             for(var i = 0, len = values.length; i < len; i++){
11732                 var v = values[i];
11733                 var f = this.findField(v.id);
11734                 if(f){
11735                     f.setValue(v.value);
11736                     if(this.trackResetOnLoad){
11737                         f.originalValue = f.getValue();
11738                     }
11739                 }
11740             }
11741         }else{ // object hash
11742             var field, id;
11743             for(id in values){
11744                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11745
11746                     if (field.setFromData &&
11747                         field.valueField &&
11748                         field.displayField &&
11749                         // combos' with local stores can
11750                         // be queried via setValue()
11751                         // to set their value..
11752                         (field.store && !field.store.isLocal)
11753                         ) {
11754                         // it's a combo
11755                         var sd = { };
11756                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11757                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11758                         field.setFromData(sd);
11759
11760                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11761                         
11762                         field.setFromData(values);
11763                         
11764                     } else {
11765                         field.setValue(values[id]);
11766                     }
11767
11768
11769                     if(this.trackResetOnLoad){
11770                         field.originalValue = field.getValue();
11771                     }
11772                 }
11773             }
11774         }
11775
11776         //Roo.each(this.childForms || [], function (f) {
11777         //    f.setValues(values);
11778         //});
11779
11780         return this;
11781     },
11782
11783     /**
11784      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11785      * they are returned as an array.
11786      * @param {Boolean} asString
11787      * @return {Object}
11788      */
11789     getValues : function(asString){
11790         //if (this.childForms) {
11791             // copy values from the child forms
11792         //    Roo.each(this.childForms, function (f) {
11793         //        this.setValues(f.getValues());
11794         //    }, this);
11795         //}
11796
11797
11798
11799         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11800         if(asString === true){
11801             return fs;
11802         }
11803         return Roo.urlDecode(fs);
11804     },
11805
11806     /**
11807      * Returns the fields in this form as an object with key/value pairs.
11808      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11809      * @return {Object}
11810      */
11811     getFieldValues : function(with_hidden)
11812     {
11813         var items = this.getItems();
11814         var ret = {};
11815         items.each(function(f){
11816             
11817             if (!f.getName()) {
11818                 return;
11819             }
11820             
11821             var v = f.getValue();
11822             
11823             if (f.inputType =='radio') {
11824                 if (typeof(ret[f.getName()]) == 'undefined') {
11825                     ret[f.getName()] = ''; // empty..
11826                 }
11827
11828                 if (!f.el.dom.checked) {
11829                     return;
11830
11831                 }
11832                 v = f.el.dom.value;
11833
11834             }
11835             
11836             if(f.xtype == 'MoneyField'){
11837                 ret[f.currencyName] = f.getCurrency();
11838             }
11839
11840             // not sure if this supported any more..
11841             if ((typeof(v) == 'object') && f.getRawValue) {
11842                 v = f.getRawValue() ; // dates..
11843             }
11844             // combo boxes where name != hiddenName...
11845             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11846                 ret[f.name] = f.getRawValue();
11847             }
11848             ret[f.getName()] = v;
11849         });
11850
11851         return ret;
11852     },
11853
11854     /**
11855      * Clears all invalid messages in this form.
11856      * @return {BasicForm} this
11857      */
11858     clearInvalid : function(){
11859         var items = this.getItems();
11860
11861         items.each(function(f){
11862            f.clearInvalid();
11863         });
11864
11865         return this;
11866     },
11867
11868     /**
11869      * Resets this form.
11870      * @return {BasicForm} this
11871      */
11872     reset : function(){
11873         var items = this.getItems();
11874         items.each(function(f){
11875             f.reset();
11876         });
11877
11878         Roo.each(this.childForms || [], function (f) {
11879             f.reset();
11880         });
11881
11882
11883         return this;
11884     },
11885     
11886     getItems : function()
11887     {
11888         var r=new Roo.util.MixedCollection(false, function(o){
11889             return o.id || (o.id = Roo.id());
11890         });
11891         var iter = function(el) {
11892             if (el.inputEl) {
11893                 r.add(el);
11894             }
11895             if (!el.items) {
11896                 return;
11897             }
11898             Roo.each(el.items,function(e) {
11899                 iter(e);
11900             });
11901         };
11902
11903         iter(this);
11904         return r;
11905     },
11906     
11907     hideFields : function(items)
11908     {
11909         Roo.each(items, function(i){
11910             
11911             var f = this.findField(i);
11912             
11913             if(!f){
11914                 return;
11915             }
11916             
11917             f.hide();
11918             
11919         }, this);
11920     },
11921     
11922     showFields : function(items)
11923     {
11924         Roo.each(items, function(i){
11925             
11926             var f = this.findField(i);
11927             
11928             if(!f){
11929                 return;
11930             }
11931             
11932             f.show();
11933             
11934         }, this);
11935     }
11936
11937 });
11938
11939 Roo.apply(Roo.bootstrap.form.Form, {
11940     
11941     popover : {
11942         
11943         padding : 5,
11944         
11945         isApplied : false,
11946         
11947         isMasked : false,
11948         
11949         form : false,
11950         
11951         target : false,
11952         
11953         toolTip : false,
11954         
11955         intervalID : false,
11956         
11957         maskEl : false,
11958         
11959         apply : function()
11960         {
11961             if(this.isApplied){
11962                 return;
11963             }
11964             
11965             this.maskEl = {
11966                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11967                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11968                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11969                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11970             };
11971             
11972             this.maskEl.top.enableDisplayMode("block");
11973             this.maskEl.left.enableDisplayMode("block");
11974             this.maskEl.bottom.enableDisplayMode("block");
11975             this.maskEl.right.enableDisplayMode("block");
11976             
11977             this.toolTip = new Roo.bootstrap.Tooltip({
11978                 cls : 'roo-form-error-popover',
11979                 alignment : {
11980                     'left' : ['r-l', [-2,0], 'right'],
11981                     'right' : ['l-r', [2,0], 'left'],
11982                     'bottom' : ['tl-bl', [0,2], 'top'],
11983                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11984                 }
11985             });
11986             
11987             this.toolTip.render(Roo.get(document.body));
11988
11989             this.toolTip.el.enableDisplayMode("block");
11990             
11991             Roo.get(document.body).on('click', function(){
11992                 this.unmask();
11993             }, this);
11994             
11995             Roo.get(document.body).on('touchstart', function(){
11996                 this.unmask();
11997             }, this);
11998             
11999             this.isApplied = true
12000         },
12001         
12002         mask : function(form, target)
12003         {
12004             this.form = form;
12005             
12006             this.target = target;
12007             
12008             if(!this.form.errorMask || !target.el){
12009                 return;
12010             }
12011             
12012             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12013             
12014             Roo.log(scrollable);
12015             
12016             var ot = this.target.el.calcOffsetsTo(scrollable);
12017             
12018             var scrollTo = ot[1] - this.form.maskOffset;
12019             
12020             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12021             
12022             scrollable.scrollTo('top', scrollTo);
12023             
12024             var box = this.target.el.getBox();
12025             Roo.log(box);
12026             var zIndex = Roo.bootstrap.Modal.zIndex++;
12027
12028             
12029             this.maskEl.top.setStyle('position', 'absolute');
12030             this.maskEl.top.setStyle('z-index', zIndex);
12031             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12032             this.maskEl.top.setLeft(0);
12033             this.maskEl.top.setTop(0);
12034             this.maskEl.top.show();
12035             
12036             this.maskEl.left.setStyle('position', 'absolute');
12037             this.maskEl.left.setStyle('z-index', zIndex);
12038             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12039             this.maskEl.left.setLeft(0);
12040             this.maskEl.left.setTop(box.y - this.padding);
12041             this.maskEl.left.show();
12042
12043             this.maskEl.bottom.setStyle('position', 'absolute');
12044             this.maskEl.bottom.setStyle('z-index', zIndex);
12045             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12046             this.maskEl.bottom.setLeft(0);
12047             this.maskEl.bottom.setTop(box.bottom + this.padding);
12048             this.maskEl.bottom.show();
12049
12050             this.maskEl.right.setStyle('position', 'absolute');
12051             this.maskEl.right.setStyle('z-index', zIndex);
12052             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12053             this.maskEl.right.setLeft(box.right + this.padding);
12054             this.maskEl.right.setTop(box.y - this.padding);
12055             this.maskEl.right.show();
12056
12057             this.toolTip.bindEl = this.target.el;
12058
12059             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12060
12061             var tip = this.target.blankText;
12062
12063             if(this.target.getValue() !== '' ) {
12064                 
12065                 if (this.target.invalidText.length) {
12066                     tip = this.target.invalidText;
12067                 } else if (this.target.regexText.length){
12068                     tip = this.target.regexText;
12069                 }
12070             }
12071
12072             this.toolTip.show(tip);
12073
12074             this.intervalID = window.setInterval(function() {
12075                 Roo.bootstrap.form.Form.popover.unmask();
12076             }, 10000);
12077
12078             window.onwheel = function(){ return false;};
12079             
12080             (function(){ this.isMasked = true; }).defer(500, this);
12081             
12082         },
12083         
12084         unmask : function()
12085         {
12086             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12087                 return;
12088             }
12089             
12090             this.maskEl.top.setStyle('position', 'absolute');
12091             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12092             this.maskEl.top.hide();
12093
12094             this.maskEl.left.setStyle('position', 'absolute');
12095             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12096             this.maskEl.left.hide();
12097
12098             this.maskEl.bottom.setStyle('position', 'absolute');
12099             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12100             this.maskEl.bottom.hide();
12101
12102             this.maskEl.right.setStyle('position', 'absolute');
12103             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12104             this.maskEl.right.hide();
12105             
12106             this.toolTip.hide();
12107             
12108             this.toolTip.el.hide();
12109             
12110             window.onwheel = function(){ return true;};
12111             
12112             if(this.intervalID){
12113                 window.clearInterval(this.intervalID);
12114                 this.intervalID = false;
12115             }
12116             
12117             this.isMasked = false;
12118             
12119         }
12120         
12121     }
12122     
12123 });
12124
12125 /*
12126  * Based on:
12127  * Ext JS Library 1.1.1
12128  * Copyright(c) 2006-2007, Ext JS, LLC.
12129  *
12130  * Originally Released Under LGPL - original licence link has changed is not relivant.
12131  *
12132  * Fork - LGPL
12133  * <script type="text/javascript">
12134  */
12135 /**
12136  * @class Roo.form.VTypes
12137  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12138  * @static
12139  */
12140 Roo.form.VTypes = function(){
12141     // closure these in so they are only created once.
12142     var alpha = /^[a-zA-Z_]+$/;
12143     var alphanum = /^[a-zA-Z0-9_]+$/;
12144     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12145     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12146
12147     // All these messages and functions are configurable
12148     return {
12149         /**
12150          * The function used to validate email addresses
12151          * @param {String} value The email address
12152          */
12153         'email' : function(v){
12154             return email.test(v);
12155         },
12156         /**
12157          * The error text to display when the email validation function returns false
12158          * @type String
12159          */
12160         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12161         /**
12162          * The keystroke filter mask to be applied on email input
12163          * @type RegExp
12164          */
12165         'emailMask' : /[a-z0-9_\.\-@]/i,
12166
12167         /**
12168          * The function used to validate URLs
12169          * @param {String} value The URL
12170          */
12171         'url' : function(v){
12172             return url.test(v);
12173         },
12174         /**
12175          * The error text to display when the url validation function returns false
12176          * @type String
12177          */
12178         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12179         
12180         /**
12181          * The function used to validate alpha values
12182          * @param {String} value The value
12183          */
12184         'alpha' : function(v){
12185             return alpha.test(v);
12186         },
12187         /**
12188          * The error text to display when the alpha validation function returns false
12189          * @type String
12190          */
12191         'alphaText' : 'This field should only contain letters and _',
12192         /**
12193          * The keystroke filter mask to be applied on alpha input
12194          * @type RegExp
12195          */
12196         'alphaMask' : /[a-z_]/i,
12197
12198         /**
12199          * The function used to validate alphanumeric values
12200          * @param {String} value The value
12201          */
12202         'alphanum' : function(v){
12203             return alphanum.test(v);
12204         },
12205         /**
12206          * The error text to display when the alphanumeric validation function returns false
12207          * @type String
12208          */
12209         'alphanumText' : 'This field should only contain letters, numbers and _',
12210         /**
12211          * The keystroke filter mask to be applied on alphanumeric input
12212          * @type RegExp
12213          */
12214         'alphanumMask' : /[a-z0-9_]/i
12215     };
12216 }();/*
12217  * - LGPL
12218  *
12219  * Input
12220  * 
12221  */
12222
12223 /**
12224  * @class Roo.bootstrap.form.Input
12225  * @extends Roo.bootstrap.Component
12226  * Bootstrap Input class
12227  * @cfg {Boolean} disabled is it disabled
12228  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12229  * @cfg {String} name name of the input
12230  * @cfg {string} fieldLabel - the label associated
12231  * @cfg {string} placeholder - placeholder to put in text.
12232  * @cfg {string} before - input group add on before
12233  * @cfg {string} after - input group add on after
12234  * @cfg {string} size - (lg|sm) or leave empty..
12235  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12236  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12237  * @cfg {Number} md colspan out of 12 for computer-sized screens
12238  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12239  * @cfg {string} value default value of the input
12240  * @cfg {Number} labelWidth set the width of label 
12241  * @cfg {Number} labellg set the width of label (1-12)
12242  * @cfg {Number} labelmd set the width of label (1-12)
12243  * @cfg {Number} labelsm set the width of label (1-12)
12244  * @cfg {Number} labelxs set the width of label (1-12)
12245  * @cfg {String} labelAlign (top|left)
12246  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12247  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12248  * @cfg {String} indicatorpos (left|right) default left
12249  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12250  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12251  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12252  * @cfg {Roo.bootstrap.Button} before Button to show before
12253  * @cfg {Roo.bootstrap.Button} afterButton to show before
12254  * @cfg {String} align (left|center|right) Default left
12255  * @cfg {Boolean} forceFeedback (true|false) Default false
12256  * 
12257  * @constructor
12258  * Create a new Input
12259  * @param {Object} config The config object
12260  */
12261
12262 Roo.bootstrap.form.Input = function(config){
12263     
12264     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12265     
12266     this.addEvents({
12267         /**
12268          * @event focus
12269          * Fires when this field receives input focus.
12270          * @param {Roo.form.Field} this
12271          */
12272         focus : true,
12273         /**
12274          * @event blur
12275          * Fires when this field loses input focus.
12276          * @param {Roo.form.Field} this
12277          */
12278         blur : true,
12279         /**
12280          * @event specialkey
12281          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12282          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12283          * @param {Roo.form.Field} this
12284          * @param {Roo.EventObject} e The event object
12285          */
12286         specialkey : true,
12287         /**
12288          * @event change
12289          * Fires just before the field blurs if the field value has changed.
12290          * @param {Roo.form.Field} this
12291          * @param {Mixed} newValue The new value
12292          * @param {Mixed} oldValue The original value
12293          */
12294         change : true,
12295         /**
12296          * @event invalid
12297          * Fires after the field has been marked as invalid.
12298          * @param {Roo.form.Field} this
12299          * @param {String} msg The validation message
12300          */
12301         invalid : true,
12302         /**
12303          * @event valid
12304          * Fires after the field has been validated with no errors.
12305          * @param {Roo.form.Field} this
12306          */
12307         valid : true,
12308          /**
12309          * @event keyup
12310          * Fires after the key up
12311          * @param {Roo.form.Field} this
12312          * @param {Roo.EventObject}  e The event Object
12313          */
12314         keyup : true,
12315         /**
12316          * @event paste
12317          * Fires after the user pastes into input
12318          * @param {Roo.form.Field} this
12319          * @param {Roo.EventObject}  e The event Object
12320          */
12321         paste : true
12322     });
12323 };
12324
12325 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12326      /**
12327      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12328       automatic validation (defaults to "keyup").
12329      */
12330     validationEvent : "keyup",
12331      /**
12332      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12333      */
12334     validateOnBlur : true,
12335     /**
12336      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12337      */
12338     validationDelay : 250,
12339      /**
12340      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12341      */
12342     focusClass : "x-form-focus",  // not needed???
12343     
12344        
12345     /**
12346      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12347      */
12348     invalidClass : "has-warning",
12349     
12350     /**
12351      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12352      */
12353     validClass : "has-success",
12354     
12355     /**
12356      * @cfg {Boolean} hasFeedback (true|false) default true
12357      */
12358     hasFeedback : true,
12359     
12360     /**
12361      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12362      */
12363     invalidFeedbackClass : "glyphicon-warning-sign",
12364     
12365     /**
12366      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12367      */
12368     validFeedbackClass : "glyphicon-ok",
12369     
12370     /**
12371      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12372      */
12373     selectOnFocus : false,
12374     
12375      /**
12376      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12377      */
12378     maskRe : null,
12379        /**
12380      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12381      */
12382     vtype : null,
12383     
12384       /**
12385      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12386      */
12387     disableKeyFilter : false,
12388     
12389        /**
12390      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12391      */
12392     disabled : false,
12393      /**
12394      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12395      */
12396     allowBlank : true,
12397     /**
12398      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12399      */
12400     blankText : "Please complete this mandatory field",
12401     
12402      /**
12403      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12404      */
12405     minLength : 0,
12406     /**
12407      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12408      */
12409     maxLength : Number.MAX_VALUE,
12410     /**
12411      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12412      */
12413     minLengthText : "The minimum length for this field is {0}",
12414     /**
12415      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12416      */
12417     maxLengthText : "The maximum length for this field is {0}",
12418   
12419     
12420     /**
12421      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12422      * If available, this function will be called only after the basic validators all return true, and will be passed the
12423      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12424      */
12425     validator : null,
12426     /**
12427      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12428      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12429      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12430      */
12431     regex : null,
12432     /**
12433      * @cfg {String} regexText -- Depricated - use Invalid Text
12434      */
12435     regexText : "",
12436     
12437     /**
12438      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12439      */
12440     invalidText : "",
12441     
12442     
12443     
12444     autocomplete: false,
12445     
12446     
12447     fieldLabel : '',
12448     inputType : 'text',
12449     
12450     name : false,
12451     placeholder: false,
12452     before : false,
12453     after : false,
12454     size : false,
12455     hasFocus : false,
12456     preventMark: false,
12457     isFormField : true,
12458     value : '',
12459     labelWidth : 2,
12460     labelAlign : false,
12461     readOnly : false,
12462     align : false,
12463     formatedValue : false,
12464     forceFeedback : false,
12465     
12466     indicatorpos : 'left',
12467     
12468     labellg : 0,
12469     labelmd : 0,
12470     labelsm : 0,
12471     labelxs : 0,
12472     
12473     capture : '',
12474     accept : '',
12475     
12476     parentLabelAlign : function()
12477     {
12478         var parent = this;
12479         while (parent.parent()) {
12480             parent = parent.parent();
12481             if (typeof(parent.labelAlign) !='undefined') {
12482                 return parent.labelAlign;
12483             }
12484         }
12485         return 'left';
12486         
12487     },
12488     
12489     getAutoCreate : function()
12490     {
12491         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12492         
12493         var id = Roo.id();
12494         
12495         var cfg = {};
12496         
12497         if(this.inputType != 'hidden'){
12498             cfg.cls = 'form-group' //input-group
12499         }
12500         
12501         var input =  {
12502             tag: 'input',
12503             id : id,
12504             type : this.inputType,
12505             value : this.value,
12506             cls : 'form-control',
12507             placeholder : this.placeholder || '',
12508             autocomplete : this.autocomplete || 'new-password'
12509         };
12510         if (this.inputType == 'file') {
12511             input.style = 'overflow:hidden'; // why not in CSS?
12512         }
12513         
12514         if(this.capture.length){
12515             input.capture = this.capture;
12516         }
12517         
12518         if(this.accept.length){
12519             input.accept = this.accept + "/*";
12520         }
12521         
12522         if(this.align){
12523             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12524         }
12525         
12526         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12527             input.maxLength = this.maxLength;
12528         }
12529         
12530         if (this.disabled) {
12531             input.disabled=true;
12532         }
12533         
12534         if (this.readOnly) {
12535             input.readonly=true;
12536         }
12537         
12538         if (this.name) {
12539             input.name = this.name;
12540         }
12541         
12542         if (this.size) {
12543             input.cls += ' input-' + this.size;
12544         }
12545         
12546         var settings=this;
12547         ['xs','sm','md','lg'].map(function(size){
12548             if (settings[size]) {
12549                 cfg.cls += ' col-' + size + '-' + settings[size];
12550             }
12551         });
12552         
12553         var inputblock = input;
12554         
12555         var feedback = {
12556             tag: 'span',
12557             cls: 'glyphicon form-control-feedback'
12558         };
12559             
12560         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12561             
12562             inputblock = {
12563                 cls : 'has-feedback',
12564                 cn :  [
12565                     input,
12566                     feedback
12567                 ] 
12568             };  
12569         }
12570         
12571         if (this.before || this.after) {
12572             
12573             inputblock = {
12574                 cls : 'input-group',
12575                 cn :  [] 
12576             };
12577             
12578             if (this.before && typeof(this.before) == 'string') {
12579                 
12580                 inputblock.cn.push({
12581                     tag :'span',
12582                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12583                     html : this.before
12584                 });
12585             }
12586             if (this.before && typeof(this.before) == 'object') {
12587                 this.before = Roo.factory(this.before);
12588                 
12589                 inputblock.cn.push({
12590                     tag :'span',
12591                     cls : 'roo-input-before input-group-prepend   input-group-' +
12592                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12593                 });
12594             }
12595             
12596             inputblock.cn.push(input);
12597             
12598             if (this.after && typeof(this.after) == 'string') {
12599                 inputblock.cn.push({
12600                     tag :'span',
12601                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12602                     html : this.after
12603                 });
12604             }
12605             if (this.after && typeof(this.after) == 'object') {
12606                 this.after = Roo.factory(this.after);
12607                 
12608                 inputblock.cn.push({
12609                     tag :'span',
12610                     cls : 'roo-input-after input-group-append  input-group-' +
12611                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12612                 });
12613             }
12614             
12615             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12616                 inputblock.cls += ' has-feedback';
12617                 inputblock.cn.push(feedback);
12618             }
12619         };
12620         var indicator = {
12621             tag : 'i',
12622             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12623             tooltip : 'This field is required'
12624         };
12625         if (this.allowBlank ) {
12626             indicator.style = this.allowBlank ? ' display:none' : '';
12627         }
12628         if (align ==='left' && this.fieldLabel.length) {
12629             
12630             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12631             
12632             cfg.cn = [
12633                 indicator,
12634                 {
12635                     tag: 'label',
12636                     'for' :  id,
12637                     cls : 'control-label col-form-label',
12638                     html : this.fieldLabel
12639
12640                 },
12641                 {
12642                     cls : "", 
12643                     cn: [
12644                         inputblock
12645                     ]
12646                 }
12647             ];
12648             
12649             var labelCfg = cfg.cn[1];
12650             var contentCfg = cfg.cn[2];
12651             
12652             if(this.indicatorpos == 'right'){
12653                 cfg.cn = [
12654                     {
12655                         tag: 'label',
12656                         'for' :  id,
12657                         cls : 'control-label col-form-label',
12658                         cn : [
12659                             {
12660                                 tag : 'span',
12661                                 html : this.fieldLabel
12662                             },
12663                             indicator
12664                         ]
12665                     },
12666                     {
12667                         cls : "",
12668                         cn: [
12669                             inputblock
12670                         ]
12671                     }
12672
12673                 ];
12674                 
12675                 labelCfg = cfg.cn[0];
12676                 contentCfg = cfg.cn[1];
12677             
12678             }
12679             
12680             if(this.labelWidth > 12){
12681                 labelCfg.style = "width: " + this.labelWidth + 'px';
12682             }
12683             
12684             if(this.labelWidth < 13 && this.labelmd == 0){
12685                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12686             }
12687             
12688             if(this.labellg > 0){
12689                 labelCfg.cls += ' col-lg-' + this.labellg;
12690                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12691             }
12692             
12693             if(this.labelmd > 0){
12694                 labelCfg.cls += ' col-md-' + this.labelmd;
12695                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12696             }
12697             
12698             if(this.labelsm > 0){
12699                 labelCfg.cls += ' col-sm-' + this.labelsm;
12700                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12701             }
12702             
12703             if(this.labelxs > 0){
12704                 labelCfg.cls += ' col-xs-' + this.labelxs;
12705                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12706             }
12707             
12708             
12709         } else if ( this.fieldLabel.length) {
12710                 
12711             
12712             
12713             cfg.cn = [
12714                 {
12715                     tag : 'i',
12716                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12717                     tooltip : 'This field is required',
12718                     style : this.allowBlank ? ' display:none' : '' 
12719                 },
12720                 {
12721                     tag: 'label',
12722                    //cls : 'input-group-addon',
12723                     html : this.fieldLabel
12724
12725                 },
12726
12727                inputblock
12728
12729            ];
12730            
12731            if(this.indicatorpos == 'right'){
12732        
12733                 cfg.cn = [
12734                     {
12735                         tag: 'label',
12736                        //cls : 'input-group-addon',
12737                         html : this.fieldLabel
12738
12739                     },
12740                     {
12741                         tag : 'i',
12742                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12743                         tooltip : 'This field is required',
12744                         style : this.allowBlank ? ' display:none' : '' 
12745                     },
12746
12747                    inputblock
12748
12749                ];
12750
12751             }
12752
12753         } else {
12754             
12755             cfg.cn = [
12756
12757                     inputblock
12758
12759             ];
12760                 
12761                 
12762         };
12763         
12764         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12765            cfg.cls += ' navbar-form';
12766         }
12767         
12768         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12769             // on BS4 we do this only if not form 
12770             cfg.cls += ' navbar-form';
12771             cfg.tag = 'li';
12772         }
12773         
12774         return cfg;
12775         
12776     },
12777     /**
12778      * return the real input element.
12779      */
12780     inputEl: function ()
12781     {
12782         return this.el.select('input.form-control',true).first();
12783     },
12784     
12785     tooltipEl : function()
12786     {
12787         return this.inputEl();
12788     },
12789     
12790     indicatorEl : function()
12791     {
12792         if (Roo.bootstrap.version == 4) {
12793             return false; // not enabled in v4 yet.
12794         }
12795         
12796         var indicator = this.el.select('i.roo-required-indicator',true).first();
12797         
12798         if(!indicator){
12799             return false;
12800         }
12801         
12802         return indicator;
12803         
12804     },
12805     
12806     setDisabled : function(v)
12807     {
12808         var i  = this.inputEl().dom;
12809         if (!v) {
12810             i.removeAttribute('disabled');
12811             return;
12812             
12813         }
12814         i.setAttribute('disabled','true');
12815     },
12816     initEvents : function()
12817     {
12818           
12819         this.inputEl().on("keydown" , this.fireKey,  this);
12820         this.inputEl().on("focus", this.onFocus,  this);
12821         this.inputEl().on("blur", this.onBlur,  this);
12822         
12823         this.inputEl().relayEvent('keyup', this);
12824         this.inputEl().relayEvent('paste', this);
12825         
12826         this.indicator = this.indicatorEl();
12827         
12828         if(this.indicator){
12829             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12830         }
12831  
12832         // reference to original value for reset
12833         this.originalValue = this.getValue();
12834         //Roo.form.TextField.superclass.initEvents.call(this);
12835         if(this.validationEvent == 'keyup'){
12836             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12837             this.inputEl().on('keyup', this.filterValidation, this);
12838         }
12839         else if(this.validationEvent !== false){
12840             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12841         }
12842         
12843         if(this.selectOnFocus){
12844             this.on("focus", this.preFocus, this);
12845             
12846         }
12847         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12848             this.inputEl().on("keypress", this.filterKeys, this);
12849         } else {
12850             this.inputEl().relayEvent('keypress', this);
12851         }
12852        /* if(this.grow){
12853             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12854             this.el.on("click", this.autoSize,  this);
12855         }
12856         */
12857         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12858             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12859         }
12860         
12861         if (typeof(this.before) == 'object') {
12862             this.before.render(this.el.select('.roo-input-before',true).first());
12863         }
12864         if (typeof(this.after) == 'object') {
12865             this.after.render(this.el.select('.roo-input-after',true).first());
12866         }
12867         
12868         this.inputEl().on('change', this.onChange, this);
12869         
12870     },
12871     filterValidation : function(e){
12872         if(!e.isNavKeyPress()){
12873             this.validationTask.delay(this.validationDelay);
12874         }
12875     },
12876      /**
12877      * Validates the field value
12878      * @return {Boolean} True if the value is valid, else false
12879      */
12880     validate : function(){
12881         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12882         if(this.disabled || this.validateValue(this.getRawValue())){
12883             this.markValid();
12884             return true;
12885         }
12886         
12887         this.markInvalid();
12888         return false;
12889     },
12890     
12891     
12892     /**
12893      * Validates a value according to the field's validation rules and marks the field as invalid
12894      * if the validation fails
12895      * @param {Mixed} value The value to validate
12896      * @return {Boolean} True if the value is valid, else false
12897      */
12898     validateValue : function(value)
12899     {
12900         if(this.getVisibilityEl().hasClass('hidden')){
12901             return true;
12902         }
12903         
12904         if(value.length < 1)  { // if it's blank
12905             if(this.allowBlank){
12906                 return true;
12907             }
12908             return false;
12909         }
12910         
12911         if(value.length < this.minLength){
12912             return false;
12913         }
12914         if(value.length > this.maxLength){
12915             return false;
12916         }
12917         if(this.vtype){
12918             var vt = Roo.form.VTypes;
12919             if(!vt[this.vtype](value, this)){
12920                 return false;
12921             }
12922         }
12923         if(typeof this.validator == "function"){
12924             var msg = this.validator(value);
12925             if(msg !== true){
12926                 return false;
12927             }
12928             if (typeof(msg) == 'string') {
12929                 this.invalidText = msg;
12930             }
12931         }
12932         
12933         if(this.regex && !this.regex.test(value)){
12934             return false;
12935         }
12936         
12937         return true;
12938     },
12939     
12940      // private
12941     fireKey : function(e){
12942         //Roo.log('field ' + e.getKey());
12943         if(e.isNavKeyPress()){
12944             this.fireEvent("specialkey", this, e);
12945         }
12946     },
12947     focus : function (selectText){
12948         if(this.rendered){
12949             this.inputEl().focus();
12950             if(selectText === true){
12951                 this.inputEl().dom.select();
12952             }
12953         }
12954         return this;
12955     } ,
12956     
12957     onFocus : function(){
12958         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12959            // this.el.addClass(this.focusClass);
12960         }
12961         if(!this.hasFocus){
12962             this.hasFocus = true;
12963             this.startValue = this.getValue();
12964             this.fireEvent("focus", this);
12965         }
12966     },
12967     
12968     beforeBlur : Roo.emptyFn,
12969
12970     
12971     // private
12972     onBlur : function(){
12973         this.beforeBlur();
12974         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12975             //this.el.removeClass(this.focusClass);
12976         }
12977         this.hasFocus = false;
12978         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12979             this.validate();
12980         }
12981         var v = this.getValue();
12982         if(String(v) !== String(this.startValue)){
12983             this.fireEvent('change', this, v, this.startValue);
12984         }
12985         this.fireEvent("blur", this);
12986     },
12987     
12988     onChange : function(e)
12989     {
12990         var v = this.getValue();
12991         if(String(v) !== String(this.startValue)){
12992             this.fireEvent('change', this, v, this.startValue);
12993         }
12994         
12995     },
12996     
12997     /**
12998      * Resets the current field value to the originally loaded value and clears any validation messages
12999      */
13000     reset : function(){
13001         this.setValue(this.originalValue);
13002         this.validate();
13003     },
13004      /**
13005      * Returns the name of the field
13006      * @return {Mixed} name The name field
13007      */
13008     getName: function(){
13009         return this.name;
13010     },
13011      /**
13012      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13013      * @return {Mixed} value The field value
13014      */
13015     getValue : function(){
13016         
13017         var v = this.inputEl().getValue();
13018         
13019         return v;
13020     },
13021     /**
13022      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13023      * @return {Mixed} value The field value
13024      */
13025     getRawValue : function(){
13026         var v = this.inputEl().getValue();
13027         
13028         return v;
13029     },
13030     
13031     /**
13032      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13033      * @param {Mixed} value The value to set
13034      */
13035     setRawValue : function(v){
13036         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13037     },
13038     
13039     selectText : function(start, end){
13040         var v = this.getRawValue();
13041         if(v.length > 0){
13042             start = start === undefined ? 0 : start;
13043             end = end === undefined ? v.length : end;
13044             var d = this.inputEl().dom;
13045             if(d.setSelectionRange){
13046                 d.setSelectionRange(start, end);
13047             }else if(d.createTextRange){
13048                 var range = d.createTextRange();
13049                 range.moveStart("character", start);
13050                 range.moveEnd("character", v.length-end);
13051                 range.select();
13052             }
13053         }
13054     },
13055     
13056     /**
13057      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13058      * @param {Mixed} value The value to set
13059      */
13060     setValue : function(v){
13061         this.value = v;
13062         if(this.rendered){
13063             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13064             this.validate();
13065         }
13066     },
13067     
13068     /*
13069     processValue : function(value){
13070         if(this.stripCharsRe){
13071             var newValue = value.replace(this.stripCharsRe, '');
13072             if(newValue !== value){
13073                 this.setRawValue(newValue);
13074                 return newValue;
13075             }
13076         }
13077         return value;
13078     },
13079   */
13080     preFocus : function(){
13081         
13082         if(this.selectOnFocus){
13083             this.inputEl().dom.select();
13084         }
13085     },
13086     filterKeys : function(e){
13087         var k = e.getKey();
13088         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13089             return;
13090         }
13091         var c = e.getCharCode(), cc = String.fromCharCode(c);
13092         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13093             return;
13094         }
13095         if(!this.maskRe.test(cc)){
13096             e.stopEvent();
13097         }
13098     },
13099      /**
13100      * Clear any invalid styles/messages for this field
13101      */
13102     clearInvalid : function(){
13103         
13104         if(!this.el || this.preventMark){ // not rendered
13105             return;
13106         }
13107         
13108         
13109         this.el.removeClass([this.invalidClass, 'is-invalid']);
13110         
13111         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13112             
13113             var feedback = this.el.select('.form-control-feedback', true).first();
13114             
13115             if(feedback){
13116                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13117             }
13118             
13119         }
13120         
13121         if(this.indicator){
13122             this.indicator.removeClass('visible');
13123             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13124         }
13125         
13126         this.fireEvent('valid', this);
13127     },
13128     
13129      /**
13130      * Mark this field as valid
13131      */
13132     markValid : function()
13133     {
13134         if(!this.el  || this.preventMark){ // not rendered...
13135             return;
13136         }
13137         
13138         this.el.removeClass([this.invalidClass, this.validClass]);
13139         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13140
13141         var feedback = this.el.select('.form-control-feedback', true).first();
13142             
13143         if(feedback){
13144             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13145         }
13146         
13147         if(this.indicator){
13148             this.indicator.removeClass('visible');
13149             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13150         }
13151         
13152         if(this.disabled){
13153             return;
13154         }
13155         
13156            
13157         if(this.allowBlank && !this.getRawValue().length){
13158             return;
13159         }
13160         if (Roo.bootstrap.version == 3) {
13161             this.el.addClass(this.validClass);
13162         } else {
13163             this.inputEl().addClass('is-valid');
13164         }
13165
13166         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13167             
13168             var feedback = this.el.select('.form-control-feedback', true).first();
13169             
13170             if(feedback){
13171                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13172                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13173             }
13174             
13175         }
13176         
13177         this.fireEvent('valid', this);
13178     },
13179     
13180      /**
13181      * Mark this field as invalid
13182      * @param {String} msg The validation message
13183      */
13184     markInvalid : function(msg)
13185     {
13186         if(!this.el  || this.preventMark){ // not rendered
13187             return;
13188         }
13189         
13190         this.el.removeClass([this.invalidClass, this.validClass]);
13191         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13192         
13193         var feedback = this.el.select('.form-control-feedback', true).first();
13194             
13195         if(feedback){
13196             this.el.select('.form-control-feedback', true).first().removeClass(
13197                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13198         }
13199
13200         if(this.disabled){
13201             return;
13202         }
13203         
13204         if(this.allowBlank && !this.getRawValue().length){
13205             return;
13206         }
13207         
13208         if(this.indicator){
13209             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13210             this.indicator.addClass('visible');
13211         }
13212         if (Roo.bootstrap.version == 3) {
13213             this.el.addClass(this.invalidClass);
13214         } else {
13215             this.inputEl().addClass('is-invalid');
13216         }
13217         
13218         
13219         
13220         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13221             
13222             var feedback = this.el.select('.form-control-feedback', true).first();
13223             
13224             if(feedback){
13225                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13226                 
13227                 if(this.getValue().length || this.forceFeedback){
13228                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13229                 }
13230                 
13231             }
13232             
13233         }
13234         
13235         this.fireEvent('invalid', this, msg);
13236     },
13237     // private
13238     SafariOnKeyDown : function(event)
13239     {
13240         // this is a workaround for a password hang bug on chrome/ webkit.
13241         if (this.inputEl().dom.type != 'password') {
13242             return;
13243         }
13244         
13245         var isSelectAll = false;
13246         
13247         if(this.inputEl().dom.selectionEnd > 0){
13248             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13249         }
13250         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13251             event.preventDefault();
13252             this.setValue('');
13253             return;
13254         }
13255         
13256         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13257             
13258             event.preventDefault();
13259             // this is very hacky as keydown always get's upper case.
13260             //
13261             var cc = String.fromCharCode(event.getCharCode());
13262             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13263             
13264         }
13265     },
13266     adjustWidth : function(tag, w){
13267         tag = tag.toLowerCase();
13268         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13269             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13270                 if(tag == 'input'){
13271                     return w + 2;
13272                 }
13273                 if(tag == 'textarea'){
13274                     return w-2;
13275                 }
13276             }else if(Roo.isOpera){
13277                 if(tag == 'input'){
13278                     return w + 2;
13279                 }
13280                 if(tag == 'textarea'){
13281                     return w-2;
13282                 }
13283             }
13284         }
13285         return w;
13286     },
13287     
13288     setFieldLabel : function(v)
13289     {
13290         if(!this.rendered){
13291             return;
13292         }
13293         
13294         if(this.indicatorEl()){
13295             var ar = this.el.select('label > span',true);
13296             
13297             if (ar.elements.length) {
13298                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13299                 this.fieldLabel = v;
13300                 return;
13301             }
13302             
13303             var br = this.el.select('label',true);
13304             
13305             if(br.elements.length) {
13306                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13307                 this.fieldLabel = v;
13308                 return;
13309             }
13310             
13311             Roo.log('Cannot Found any of label > span || label in input');
13312             return;
13313         }
13314         
13315         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13316         this.fieldLabel = v;
13317         
13318         
13319     }
13320 });
13321
13322  
13323 /*
13324  * - LGPL
13325  *
13326  * Input
13327  * 
13328  */
13329
13330 /**
13331  * @class Roo.bootstrap.form.TextArea
13332  * @extends Roo.bootstrap.form.Input
13333  * Bootstrap TextArea class
13334  * @cfg {Number} cols Specifies the visible width of a text area
13335  * @cfg {Number} rows Specifies the visible number of lines in a text area
13336  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13337  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13338  * @cfg {string} html text
13339  * 
13340  * @constructor
13341  * Create a new TextArea
13342  * @param {Object} config The config object
13343  */
13344
13345 Roo.bootstrap.form.TextArea = function(config){
13346     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13347    
13348 };
13349
13350 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13351      
13352     cols : false,
13353     rows : 5,
13354     readOnly : false,
13355     warp : 'soft',
13356     resize : false,
13357     value: false,
13358     html: false,
13359     
13360     getAutoCreate : function(){
13361         
13362         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13363         
13364         var id = Roo.id();
13365         
13366         var cfg = {};
13367         
13368         if(this.inputType != 'hidden'){
13369             cfg.cls = 'form-group' //input-group
13370         }
13371         
13372         var input =  {
13373             tag: 'textarea',
13374             id : id,
13375             warp : this.warp,
13376             rows : this.rows,
13377             value : this.value || '',
13378             html: this.html || '',
13379             cls : 'form-control',
13380             placeholder : this.placeholder || '' 
13381             
13382         };
13383         
13384         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13385             input.maxLength = this.maxLength;
13386         }
13387         
13388         if(this.resize){
13389             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13390         }
13391         
13392         if(this.cols){
13393             input.cols = this.cols;
13394         }
13395         
13396         if (this.readOnly) {
13397             input.readonly = true;
13398         }
13399         
13400         if (this.name) {
13401             input.name = this.name;
13402         }
13403         
13404         if (this.size) {
13405             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13406         }
13407         
13408         var settings=this;
13409         ['xs','sm','md','lg'].map(function(size){
13410             if (settings[size]) {
13411                 cfg.cls += ' col-' + size + '-' + settings[size];
13412             }
13413         });
13414         
13415         var inputblock = input;
13416         
13417         if(this.hasFeedback && !this.allowBlank){
13418             
13419             var feedback = {
13420                 tag: 'span',
13421                 cls: 'glyphicon form-control-feedback'
13422             };
13423
13424             inputblock = {
13425                 cls : 'has-feedback',
13426                 cn :  [
13427                     input,
13428                     feedback
13429                 ] 
13430             };  
13431         }
13432         
13433         
13434         if (this.before || this.after) {
13435             
13436             inputblock = {
13437                 cls : 'input-group',
13438                 cn :  [] 
13439             };
13440             if (this.before) {
13441                 inputblock.cn.push({
13442                     tag :'span',
13443                     cls : 'input-group-addon',
13444                     html : this.before
13445                 });
13446             }
13447             
13448             inputblock.cn.push(input);
13449             
13450             if(this.hasFeedback && !this.allowBlank){
13451                 inputblock.cls += ' has-feedback';
13452                 inputblock.cn.push(feedback);
13453             }
13454             
13455             if (this.after) {
13456                 inputblock.cn.push({
13457                     tag :'span',
13458                     cls : 'input-group-addon',
13459                     html : this.after
13460                 });
13461             }
13462             
13463         }
13464         
13465         if (align ==='left' && this.fieldLabel.length) {
13466             cfg.cn = [
13467                 {
13468                     tag: 'label',
13469                     'for' :  id,
13470                     cls : 'control-label',
13471                     html : this.fieldLabel
13472                 },
13473                 {
13474                     cls : "",
13475                     cn: [
13476                         inputblock
13477                     ]
13478                 }
13479
13480             ];
13481             
13482             if(this.labelWidth > 12){
13483                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13484             }
13485
13486             if(this.labelWidth < 13 && this.labelmd == 0){
13487                 this.labelmd = this.labelWidth;
13488             }
13489
13490             if(this.labellg > 0){
13491                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13492                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13493             }
13494
13495             if(this.labelmd > 0){
13496                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13497                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13498             }
13499
13500             if(this.labelsm > 0){
13501                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13502                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13503             }
13504
13505             if(this.labelxs > 0){
13506                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13507                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13508             }
13509             
13510         } else if ( this.fieldLabel.length) {
13511             cfg.cn = [
13512
13513                {
13514                    tag: 'label',
13515                    //cls : 'input-group-addon',
13516                    html : this.fieldLabel
13517
13518                },
13519
13520                inputblock
13521
13522            ];
13523
13524         } else {
13525
13526             cfg.cn = [
13527
13528                 inputblock
13529
13530             ];
13531                 
13532         }
13533         
13534         if (this.disabled) {
13535             input.disabled=true;
13536         }
13537         
13538         return cfg;
13539         
13540     },
13541     /**
13542      * return the real textarea element.
13543      */
13544     inputEl: function ()
13545     {
13546         return this.el.select('textarea.form-control',true).first();
13547     },
13548     
13549     /**
13550      * Clear any invalid styles/messages for this field
13551      */
13552     clearInvalid : function()
13553     {
13554         
13555         if(!this.el || this.preventMark){ // not rendered
13556             return;
13557         }
13558         
13559         var label = this.el.select('label', true).first();
13560         var icon = this.el.select('i.fa-star', true).first();
13561         
13562         if(label && icon){
13563             icon.remove();
13564         }
13565         this.el.removeClass( this.validClass);
13566         this.inputEl().removeClass('is-invalid');
13567          
13568         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13569             
13570             var feedback = this.el.select('.form-control-feedback', true).first();
13571             
13572             if(feedback){
13573                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13574             }
13575             
13576         }
13577         
13578         this.fireEvent('valid', this);
13579     },
13580     
13581      /**
13582      * Mark this field as valid
13583      */
13584     markValid : function()
13585     {
13586         if(!this.el  || this.preventMark){ // not rendered
13587             return;
13588         }
13589         
13590         this.el.removeClass([this.invalidClass, this.validClass]);
13591         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13592         
13593         var feedback = this.el.select('.form-control-feedback', true).first();
13594             
13595         if(feedback){
13596             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13597         }
13598
13599         if(this.disabled || this.allowBlank){
13600             return;
13601         }
13602         
13603         var label = this.el.select('label', true).first();
13604         var icon = this.el.select('i.fa-star', true).first();
13605         
13606         if(label && icon){
13607             icon.remove();
13608         }
13609         if (Roo.bootstrap.version == 3) {
13610             this.el.addClass(this.validClass);
13611         } else {
13612             this.inputEl().addClass('is-valid');
13613         }
13614         
13615         
13616         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13617             
13618             var feedback = this.el.select('.form-control-feedback', true).first();
13619             
13620             if(feedback){
13621                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13622                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13623             }
13624             
13625         }
13626         
13627         this.fireEvent('valid', this);
13628     },
13629     
13630      /**
13631      * Mark this field as invalid
13632      * @param {String} msg The validation message
13633      */
13634     markInvalid : function(msg)
13635     {
13636         if(!this.el  || this.preventMark){ // not rendered
13637             return;
13638         }
13639         
13640         this.el.removeClass([this.invalidClass, this.validClass]);
13641         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13642         
13643         var feedback = this.el.select('.form-control-feedback', true).first();
13644             
13645         if(feedback){
13646             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13647         }
13648
13649         if(this.disabled || this.allowBlank){
13650             return;
13651         }
13652         
13653         var label = this.el.select('label', true).first();
13654         var icon = this.el.select('i.fa-star', true).first();
13655         
13656         if(!this.getValue().length && label && !icon){
13657             this.el.createChild({
13658                 tag : 'i',
13659                 cls : 'text-danger fa fa-lg fa-star',
13660                 tooltip : 'This field is required',
13661                 style : 'margin-right:5px;'
13662             }, label, true);
13663         }
13664         
13665         if (Roo.bootstrap.version == 3) {
13666             this.el.addClass(this.invalidClass);
13667         } else {
13668             this.inputEl().addClass('is-invalid');
13669         }
13670         
13671         // fixme ... this may be depricated need to test..
13672         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13673             
13674             var feedback = this.el.select('.form-control-feedback', true).first();
13675             
13676             if(feedback){
13677                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13678                 
13679                 if(this.getValue().length || this.forceFeedback){
13680                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13681                 }
13682                 
13683             }
13684             
13685         }
13686         
13687         this.fireEvent('invalid', this, msg);
13688     }
13689 });
13690
13691  
13692 /*
13693  * - LGPL
13694  *
13695  * trigger field - base class for combo..
13696  * 
13697  */
13698  
13699 /**
13700  * @class Roo.bootstrap.form.TriggerField
13701  * @extends Roo.bootstrap.form.Input
13702  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13703  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13704  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13705  * for which you can provide a custom implementation.  For example:
13706  * <pre><code>
13707 var trigger = new Roo.bootstrap.form.TriggerField();
13708 trigger.onTriggerClick = myTriggerFn;
13709 trigger.applyTo('my-field');
13710 </code></pre>
13711  *
13712  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13713  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13714  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13715  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13716  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13717
13718  * @constructor
13719  * Create a new TriggerField.
13720  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13721  * to the base TextField)
13722  */
13723 Roo.bootstrap.form.TriggerField = function(config){
13724     this.mimicing = false;
13725     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13726 };
13727
13728 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13729     /**
13730      * @cfg {String} triggerClass A CSS class to apply to the trigger
13731      */
13732      /**
13733      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13734      */
13735     hideTrigger:false,
13736
13737     /**
13738      * @cfg {Boolean} removable (true|false) special filter default false
13739      */
13740     removable : false,
13741     
13742     /** @cfg {Boolean} grow @hide */
13743     /** @cfg {Number} growMin @hide */
13744     /** @cfg {Number} growMax @hide */
13745
13746     /**
13747      * @hide 
13748      * @method
13749      */
13750     autoSize: Roo.emptyFn,
13751     // private
13752     monitorTab : true,
13753     // private
13754     deferHeight : true,
13755
13756     
13757     actionMode : 'wrap',
13758     
13759     caret : false,
13760     
13761     
13762     getAutoCreate : function(){
13763        
13764         var align = this.labelAlign || this.parentLabelAlign();
13765         
13766         var id = Roo.id();
13767         
13768         var cfg = {
13769             cls: 'form-group' //input-group
13770         };
13771         
13772         
13773         var input =  {
13774             tag: 'input',
13775             id : id,
13776             type : this.inputType,
13777             cls : 'form-control',
13778             autocomplete: 'new-password',
13779             placeholder : this.placeholder || '' 
13780             
13781         };
13782         if (this.name) {
13783             input.name = this.name;
13784         }
13785         if (this.size) {
13786             input.cls += ' input-' + this.size;
13787         }
13788         
13789         if (this.disabled) {
13790             input.disabled=true;
13791         }
13792         
13793         var inputblock = input;
13794         
13795         if(this.hasFeedback && !this.allowBlank){
13796             
13797             var feedback = {
13798                 tag: 'span',
13799                 cls: 'glyphicon form-control-feedback'
13800             };
13801             
13802             if(this.removable && !this.editable  ){
13803                 inputblock = {
13804                     cls : 'has-feedback',
13805                     cn :  [
13806                         inputblock,
13807                         {
13808                             tag: 'button',
13809                             html : 'x',
13810                             cls : 'roo-combo-removable-btn close'
13811                         },
13812                         feedback
13813                     ] 
13814                 };
13815             } else {
13816                 inputblock = {
13817                     cls : 'has-feedback',
13818                     cn :  [
13819                         inputblock,
13820                         feedback
13821                     ] 
13822                 };
13823             }
13824
13825         } else {
13826             if(this.removable && !this.editable ){
13827                 inputblock = {
13828                     cls : 'roo-removable',
13829                     cn :  [
13830                         inputblock,
13831                         {
13832                             tag: 'button',
13833                             html : 'x',
13834                             cls : 'roo-combo-removable-btn close'
13835                         }
13836                     ] 
13837                 };
13838             }
13839         }
13840         
13841         if (this.before || this.after) {
13842             
13843             inputblock = {
13844                 cls : 'input-group',
13845                 cn :  [] 
13846             };
13847             if (this.before) {
13848                 inputblock.cn.push({
13849                     tag :'span',
13850                     cls : 'input-group-addon input-group-prepend input-group-text',
13851                     html : this.before
13852                 });
13853             }
13854             
13855             inputblock.cn.push(input);
13856             
13857             if(this.hasFeedback && !this.allowBlank){
13858                 inputblock.cls += ' has-feedback';
13859                 inputblock.cn.push(feedback);
13860             }
13861             
13862             if (this.after) {
13863                 inputblock.cn.push({
13864                     tag :'span',
13865                     cls : 'input-group-addon input-group-append input-group-text',
13866                     html : this.after
13867                 });
13868             }
13869             
13870         };
13871         
13872       
13873         
13874         var ibwrap = inputblock;
13875         
13876         if(this.multiple){
13877             ibwrap = {
13878                 tag: 'ul',
13879                 cls: 'roo-select2-choices',
13880                 cn:[
13881                     {
13882                         tag: 'li',
13883                         cls: 'roo-select2-search-field',
13884                         cn: [
13885
13886                             inputblock
13887                         ]
13888                     }
13889                 ]
13890             };
13891                 
13892         }
13893         
13894         var combobox = {
13895             cls: 'roo-select2-container input-group',
13896             cn: [
13897                  {
13898                     tag: 'input',
13899                     type : 'hidden',
13900                     cls: 'form-hidden-field'
13901                 },
13902                 ibwrap
13903             ]
13904         };
13905         
13906         if(!this.multiple && this.showToggleBtn){
13907             
13908             var caret = {
13909                         tag: 'span',
13910                         cls: 'caret'
13911              };
13912             if (this.caret != false) {
13913                 caret = {
13914                      tag: 'i',
13915                      cls: 'fa fa-' + this.caret
13916                 };
13917                 
13918             }
13919             
13920             combobox.cn.push({
13921                 tag :'span',
13922                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13923                 cn : [
13924                     Roo.bootstrap.version == 3 ? caret : '',
13925                     {
13926                         tag: 'span',
13927                         cls: 'combobox-clear',
13928                         cn  : [
13929                             {
13930                                 tag : 'i',
13931                                 cls: 'icon-remove'
13932                             }
13933                         ]
13934                     }
13935                 ]
13936
13937             })
13938         }
13939         
13940         if(this.multiple){
13941             combobox.cls += ' roo-select2-container-multi';
13942         }
13943          var indicator = {
13944             tag : 'i',
13945             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13946             tooltip : 'This field is required'
13947         };
13948         if (Roo.bootstrap.version == 4) {
13949             indicator = {
13950                 tag : 'i',
13951                 style : 'display:none'
13952             };
13953         }
13954         
13955         
13956         if (align ==='left' && this.fieldLabel.length) {
13957             
13958             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13959
13960             cfg.cn = [
13961                 indicator,
13962                 {
13963                     tag: 'label',
13964                     'for' :  id,
13965                     cls : 'control-label',
13966                     html : this.fieldLabel
13967
13968                 },
13969                 {
13970                     cls : "", 
13971                     cn: [
13972                         combobox
13973                     ]
13974                 }
13975
13976             ];
13977             
13978             var labelCfg = cfg.cn[1];
13979             var contentCfg = cfg.cn[2];
13980             
13981             if(this.indicatorpos == 'right'){
13982                 cfg.cn = [
13983                     {
13984                         tag: 'label',
13985                         'for' :  id,
13986                         cls : 'control-label',
13987                         cn : [
13988                             {
13989                                 tag : 'span',
13990                                 html : this.fieldLabel
13991                             },
13992                             indicator
13993                         ]
13994                     },
13995                     {
13996                         cls : "", 
13997                         cn: [
13998                             combobox
13999                         ]
14000                     }
14001
14002                 ];
14003                 
14004                 labelCfg = cfg.cn[0];
14005                 contentCfg = cfg.cn[1];
14006             }
14007             
14008             if(this.labelWidth > 12){
14009                 labelCfg.style = "width: " + this.labelWidth + 'px';
14010             }
14011             
14012             if(this.labelWidth < 13 && this.labelmd == 0){
14013                 this.labelmd = this.labelWidth;
14014             }
14015             
14016             if(this.labellg > 0){
14017                 labelCfg.cls += ' col-lg-' + this.labellg;
14018                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14019             }
14020             
14021             if(this.labelmd > 0){
14022                 labelCfg.cls += ' col-md-' + this.labelmd;
14023                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14024             }
14025             
14026             if(this.labelsm > 0){
14027                 labelCfg.cls += ' col-sm-' + this.labelsm;
14028                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14029             }
14030             
14031             if(this.labelxs > 0){
14032                 labelCfg.cls += ' col-xs-' + this.labelxs;
14033                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14034             }
14035             
14036         } else if ( this.fieldLabel.length) {
14037 //                Roo.log(" label");
14038             cfg.cn = [
14039                 indicator,
14040                {
14041                    tag: 'label',
14042                    //cls : 'input-group-addon',
14043                    html : this.fieldLabel
14044
14045                },
14046
14047                combobox
14048
14049             ];
14050             
14051             if(this.indicatorpos == 'right'){
14052                 
14053                 cfg.cn = [
14054                     {
14055                        tag: 'label',
14056                        cn : [
14057                            {
14058                                tag : 'span',
14059                                html : this.fieldLabel
14060                            },
14061                            indicator
14062                        ]
14063
14064                     },
14065                     combobox
14066
14067                 ];
14068
14069             }
14070
14071         } else {
14072             
14073 //                Roo.log(" no label && no align");
14074                 cfg = combobox
14075                      
14076                 
14077         }
14078         
14079         var settings=this;
14080         ['xs','sm','md','lg'].map(function(size){
14081             if (settings[size]) {
14082                 cfg.cls += ' col-' + size + '-' + settings[size];
14083             }
14084         });
14085         
14086         return cfg;
14087         
14088     },
14089     
14090     
14091     
14092     // private
14093     onResize : function(w, h){
14094 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14095 //        if(typeof w == 'number'){
14096 //            var x = w - this.trigger.getWidth();
14097 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14098 //            this.trigger.setStyle('left', x+'px');
14099 //        }
14100     },
14101
14102     // private
14103     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14104
14105     // private
14106     getResizeEl : function(){
14107         return this.inputEl();
14108     },
14109
14110     // private
14111     getPositionEl : function(){
14112         return this.inputEl();
14113     },
14114
14115     // private
14116     alignErrorIcon : function(){
14117         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14118     },
14119
14120     // private
14121     initEvents : function(){
14122         
14123         this.createList();
14124         
14125         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14126         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14127         if(!this.multiple && this.showToggleBtn){
14128             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14129             if(this.hideTrigger){
14130                 this.trigger.setDisplayed(false);
14131             }
14132             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14133         }
14134         
14135         if(this.multiple){
14136             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14137         }
14138         
14139         if(this.removable && !this.editable && !this.tickable){
14140             var close = this.closeTriggerEl();
14141             
14142             if(close){
14143                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14144                 close.on('click', this.removeBtnClick, this, close);
14145             }
14146         }
14147         
14148         //this.trigger.addClassOnOver('x-form-trigger-over');
14149         //this.trigger.addClassOnClick('x-form-trigger-click');
14150         
14151         //if(!this.width){
14152         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14153         //}
14154     },
14155     
14156     closeTriggerEl : function()
14157     {
14158         var close = this.el.select('.roo-combo-removable-btn', true).first();
14159         return close ? close : false;
14160     },
14161     
14162     removeBtnClick : function(e, h, el)
14163     {
14164         e.preventDefault();
14165         
14166         if(this.fireEvent("remove", this) !== false){
14167             this.reset();
14168             this.fireEvent("afterremove", this)
14169         }
14170     },
14171     
14172     createList : function()
14173     {
14174         this.list = Roo.get(document.body).createChild({
14175             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14176             cls: 'typeahead typeahead-long dropdown-menu shadow',
14177             style: 'display:none'
14178         });
14179         
14180         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14181         
14182     },
14183
14184     // private
14185     initTrigger : function(){
14186        
14187     },
14188
14189     // private
14190     onDestroy : function(){
14191         if(this.trigger){
14192             this.trigger.removeAllListeners();
14193           //  this.trigger.remove();
14194         }
14195         //if(this.wrap){
14196         //    this.wrap.remove();
14197         //}
14198         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14199     },
14200
14201     // private
14202     onFocus : function(){
14203         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14204         /*
14205         if(!this.mimicing){
14206             this.wrap.addClass('x-trigger-wrap-focus');
14207             this.mimicing = true;
14208             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14209             if(this.monitorTab){
14210                 this.el.on("keydown", this.checkTab, this);
14211             }
14212         }
14213         */
14214     },
14215
14216     // private
14217     checkTab : function(e){
14218         if(e.getKey() == e.TAB){
14219             this.triggerBlur();
14220         }
14221     },
14222
14223     // private
14224     onBlur : function(){
14225         // do nothing
14226     },
14227
14228     // private
14229     mimicBlur : function(e, t){
14230         /*
14231         if(!this.wrap.contains(t) && this.validateBlur()){
14232             this.triggerBlur();
14233         }
14234         */
14235     },
14236
14237     // private
14238     triggerBlur : function(){
14239         this.mimicing = false;
14240         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14241         if(this.monitorTab){
14242             this.el.un("keydown", this.checkTab, this);
14243         }
14244         //this.wrap.removeClass('x-trigger-wrap-focus');
14245         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14246     },
14247
14248     // private
14249     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14250     validateBlur : function(e, t){
14251         return true;
14252     },
14253
14254     // private
14255     onDisable : function(){
14256         this.inputEl().dom.disabled = true;
14257         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14258         //if(this.wrap){
14259         //    this.wrap.addClass('x-item-disabled');
14260         //}
14261     },
14262
14263     // private
14264     onEnable : function(){
14265         this.inputEl().dom.disabled = false;
14266         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14267         //if(this.wrap){
14268         //    this.el.removeClass('x-item-disabled');
14269         //}
14270     },
14271
14272     // private
14273     onShow : function(){
14274         var ae = this.getActionEl();
14275         
14276         if(ae){
14277             ae.dom.style.display = '';
14278             ae.dom.style.visibility = 'visible';
14279         }
14280     },
14281
14282     // private
14283     
14284     onHide : function(){
14285         var ae = this.getActionEl();
14286         ae.dom.style.display = 'none';
14287     },
14288
14289     /**
14290      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14291      * by an implementing function.
14292      * @method
14293      * @param {EventObject} e
14294      */
14295     onTriggerClick : Roo.emptyFn
14296 });
14297  
14298 /*
14299 * Licence: LGPL
14300 */
14301
14302 /**
14303  * @class Roo.bootstrap.form.CardUploader
14304  * @extends Roo.bootstrap.Button
14305  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14306  * @cfg {Number} errorTimeout default 3000
14307  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14308  * @cfg {Array}  html The button text.
14309
14310  *
14311  * @constructor
14312  * Create a new CardUploader
14313  * @param {Object} config The config object
14314  */
14315
14316 Roo.bootstrap.form.CardUploader = function(config){
14317     
14318  
14319     
14320     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14321     
14322     
14323     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14324         return r.data.id
14325      });
14326     
14327      this.addEvents({
14328          // raw events
14329         /**
14330          * @event preview
14331          * When a image is clicked on - and needs to display a slideshow or similar..
14332          * @param {Roo.bootstrap.Card} this
14333          * @param {Object} The image information data 
14334          *
14335          */
14336         'preview' : true,
14337          /**
14338          * @event download
14339          * When a the download link is clicked
14340          * @param {Roo.bootstrap.Card} this
14341          * @param {Object} The image information data  contains 
14342          */
14343         'download' : true
14344         
14345     });
14346 };
14347  
14348 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14349     
14350      
14351     errorTimeout : 3000,
14352      
14353     images : false,
14354    
14355     fileCollection : false,
14356     allowBlank : true,
14357     
14358     getAutoCreate : function()
14359     {
14360         
14361         var cfg =  {
14362             cls :'form-group' ,
14363             cn : [
14364                
14365                 {
14366                     tag: 'label',
14367                    //cls : 'input-group-addon',
14368                     html : this.fieldLabel
14369
14370                 },
14371
14372                 {
14373                     tag: 'input',
14374                     type : 'hidden',
14375                     name : this.name,
14376                     value : this.value,
14377                     cls : 'd-none  form-control'
14378                 },
14379                 
14380                 {
14381                     tag: 'input',
14382                     multiple : 'multiple',
14383                     type : 'file',
14384                     cls : 'd-none  roo-card-upload-selector'
14385                 },
14386                 
14387                 {
14388                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14389                 },
14390                 {
14391                     cls : 'card-columns roo-card-uploader-container'
14392                 }
14393
14394             ]
14395         };
14396            
14397          
14398         return cfg;
14399     },
14400     
14401     getChildContainer : function() /// what children are added to.
14402     {
14403         return this.containerEl;
14404     },
14405    
14406     getButtonContainer : function() /// what children are added to.
14407     {
14408         return this.el.select(".roo-card-uploader-button-container").first();
14409     },
14410    
14411     initEvents : function()
14412     {
14413         
14414         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14415         
14416         var t = this;
14417         this.addxtype({
14418             xns: Roo.bootstrap,
14419
14420             xtype : 'Button',
14421             container_method : 'getButtonContainer' ,            
14422             html :  this.html, // fix changable?
14423             cls : 'w-100 ',
14424             listeners : {
14425                 'click' : function(btn, e) {
14426                     t.onClick(e);
14427                 }
14428             }
14429         });
14430         
14431         
14432         
14433         
14434         this.urlAPI = (window.createObjectURL && window) || 
14435                                 (window.URL && URL.revokeObjectURL && URL) || 
14436                                 (window.webkitURL && webkitURL);
14437                         
14438          
14439          
14440          
14441         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14442         
14443         this.selectorEl.on('change', this.onFileSelected, this);
14444         if (this.images) {
14445             var t = this;
14446             this.images.forEach(function(img) {
14447                 t.addCard(img)
14448             });
14449             this.images = false;
14450         }
14451         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14452          
14453        
14454     },
14455     
14456    
14457     onClick : function(e)
14458     {
14459         e.preventDefault();
14460          
14461         this.selectorEl.dom.click();
14462          
14463     },
14464     
14465     onFileSelected : function(e)
14466     {
14467         e.preventDefault();
14468         
14469         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14470             return;
14471         }
14472         
14473         Roo.each(this.selectorEl.dom.files, function(file){    
14474             this.addFile(file);
14475         }, this);
14476          
14477     },
14478     
14479       
14480     
14481       
14482     
14483     addFile : function(file)
14484     {
14485            
14486         if(typeof(file) === 'string'){
14487             throw "Add file by name?"; // should not happen
14488             return;
14489         }
14490         
14491         if(!file || !this.urlAPI){
14492             return;
14493         }
14494         
14495         // file;
14496         // file.type;
14497         
14498         var _this = this;
14499         
14500         
14501         var url = _this.urlAPI.createObjectURL( file);
14502            
14503         this.addCard({
14504             id : Roo.bootstrap.form.CardUploader.ID--,
14505             is_uploaded : false,
14506             src : url,
14507             srcfile : file,
14508             title : file.name,
14509             mimetype : file.type,
14510             preview : false,
14511             is_deleted : 0
14512         });
14513         
14514     },
14515     
14516     /**
14517      * addCard - add an Attachment to the uploader
14518      * @param data - the data about the image to upload
14519      *
14520      * {
14521           id : 123
14522           title : "Title of file",
14523           is_uploaded : false,
14524           src : "http://.....",
14525           srcfile : { the File upload object },
14526           mimetype : file.type,
14527           preview : false,
14528           is_deleted : 0
14529           .. any other data...
14530         }
14531      *
14532      * 
14533     */
14534     
14535     addCard : function (data)
14536     {
14537         // hidden input element?
14538         // if the file is not an image...
14539         //then we need to use something other that and header_image
14540         var t = this;
14541         //   remove.....
14542         var footer = [
14543             {
14544                 xns : Roo.bootstrap,
14545                 xtype : 'CardFooter',
14546                  items: [
14547                     {
14548                         xns : Roo.bootstrap,
14549                         xtype : 'Element',
14550                         cls : 'd-flex',
14551                         items : [
14552                             
14553                             {
14554                                 xns : Roo.bootstrap,
14555                                 xtype : 'Button',
14556                                 html : String.format("<small>{0}</small>", data.title),
14557                                 cls : 'col-10 text-left',
14558                                 size: 'sm',
14559                                 weight: 'link',
14560                                 fa : 'download',
14561                                 listeners : {
14562                                     click : function() {
14563                                      
14564                                         t.fireEvent( "download", t, data );
14565                                     }
14566                                 }
14567                             },
14568                           
14569                             {
14570                                 xns : Roo.bootstrap,
14571                                 xtype : 'Button',
14572                                 style: 'max-height: 28px; ',
14573                                 size : 'sm',
14574                                 weight: 'danger',
14575                                 cls : 'col-2',
14576                                 fa : 'times',
14577                                 listeners : {
14578                                     click : function() {
14579                                         t.removeCard(data.id)
14580                                     }
14581                                 }
14582                             }
14583                         ]
14584                     }
14585                     
14586                 ] 
14587             }
14588             
14589         ];
14590         
14591         var cn = this.addxtype(
14592             {
14593                  
14594                 xns : Roo.bootstrap,
14595                 xtype : 'Card',
14596                 closeable : true,
14597                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14598                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14599                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14600                 data : data,
14601                 html : false,
14602                  
14603                 items : footer,
14604                 initEvents : function() {
14605                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14606                     var card = this;
14607                     this.imgEl = this.el.select('.card-img-top').first();
14608                     if (this.imgEl) {
14609                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14610                         this.imgEl.set({ 'pointer' : 'cursor' });
14611                                   
14612                     }
14613                     this.getCardFooter().addClass('p-1');
14614                     
14615                   
14616                 }
14617                 
14618             }
14619         );
14620         // dont' really need ot update items.
14621         // this.items.push(cn);
14622         this.fileCollection.add(cn);
14623         
14624         if (!data.srcfile) {
14625             this.updateInput();
14626             return;
14627         }
14628             
14629         var _t = this;
14630         var reader = new FileReader();
14631         reader.addEventListener("load", function() {  
14632             data.srcdata =  reader.result;
14633             _t.updateInput();
14634         });
14635         reader.readAsDataURL(data.srcfile);
14636         
14637         
14638         
14639     },
14640     removeCard : function(id)
14641     {
14642         
14643         var card  = this.fileCollection.get(id);
14644         card.data.is_deleted = 1;
14645         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14646         //this.fileCollection.remove(card);
14647         //this.items = this.items.filter(function(e) { return e != card });
14648         // dont' really need ot update items.
14649         card.el.dom.parentNode.removeChild(card.el.dom);
14650         this.updateInput();
14651
14652         
14653     },
14654     reset: function()
14655     {
14656         this.fileCollection.each(function(card) {
14657             if (card.el.dom && card.el.dom.parentNode) {
14658                 card.el.dom.parentNode.removeChild(card.el.dom);
14659             }
14660         });
14661         this.fileCollection.clear();
14662         this.updateInput();
14663     },
14664     
14665     updateInput : function()
14666     {
14667          var data = [];
14668         this.fileCollection.each(function(e) {
14669             data.push(e.data);
14670             
14671         });
14672         this.inputEl().dom.value = JSON.stringify(data);
14673         
14674         
14675         
14676     }
14677     
14678     
14679 });
14680
14681
14682 Roo.bootstrap.form.CardUploader.ID = -1;/*
14683  * Based on:
14684  * Ext JS Library 1.1.1
14685  * Copyright(c) 2006-2007, Ext JS, LLC.
14686  *
14687  * Originally Released Under LGPL - original licence link has changed is not relivant.
14688  *
14689  * Fork - LGPL
14690  * <script type="text/javascript">
14691  */
14692
14693
14694 /**
14695  * @class Roo.data.SortTypes
14696  * @static
14697  * Defines the default sorting (casting?) comparison functions used when sorting data.
14698  */
14699 Roo.data.SortTypes = {
14700     /**
14701      * Default sort that does nothing
14702      * @param {Mixed} s The value being converted
14703      * @return {Mixed} The comparison value
14704      */
14705     none : function(s){
14706         return s;
14707     },
14708     
14709     /**
14710      * The regular expression used to strip tags
14711      * @type {RegExp}
14712      * @property
14713      */
14714     stripTagsRE : /<\/?[^>]+>/gi,
14715     
14716     /**
14717      * Strips all HTML tags to sort on text only
14718      * @param {Mixed} s The value being converted
14719      * @return {String} The comparison value
14720      */
14721     asText : function(s){
14722         return String(s).replace(this.stripTagsRE, "");
14723     },
14724     
14725     /**
14726      * Strips all HTML tags to sort on text only - Case insensitive
14727      * @param {Mixed} s The value being converted
14728      * @return {String} The comparison value
14729      */
14730     asUCText : function(s){
14731         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14732     },
14733     
14734     /**
14735      * Case insensitive string
14736      * @param {Mixed} s The value being converted
14737      * @return {String} The comparison value
14738      */
14739     asUCString : function(s) {
14740         return String(s).toUpperCase();
14741     },
14742     
14743     /**
14744      * Date sorting
14745      * @param {Mixed} s The value being converted
14746      * @return {Number} The comparison value
14747      */
14748     asDate : function(s) {
14749         if(!s){
14750             return 0;
14751         }
14752         if(s instanceof Date){
14753             return s.getTime();
14754         }
14755         return Date.parse(String(s));
14756     },
14757     
14758     /**
14759      * Float sorting
14760      * @param {Mixed} s The value being converted
14761      * @return {Float} The comparison value
14762      */
14763     asFloat : function(s) {
14764         var val = parseFloat(String(s).replace(/,/g, ""));
14765         if(isNaN(val)) {
14766             val = 0;
14767         }
14768         return val;
14769     },
14770     
14771     /**
14772      * Integer sorting
14773      * @param {Mixed} s The value being converted
14774      * @return {Number} The comparison value
14775      */
14776     asInt : function(s) {
14777         var val = parseInt(String(s).replace(/,/g, ""));
14778         if(isNaN(val)) {
14779             val = 0;
14780         }
14781         return val;
14782     }
14783 };/*
14784  * Based on:
14785  * Ext JS Library 1.1.1
14786  * Copyright(c) 2006-2007, Ext JS, LLC.
14787  *
14788  * Originally Released Under LGPL - original licence link has changed is not relivant.
14789  *
14790  * Fork - LGPL
14791  * <script type="text/javascript">
14792  */
14793
14794 /**
14795 * @class Roo.data.Record
14796  * Instances of this class encapsulate both record <em>definition</em> information, and record
14797  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14798  * to access Records cached in an {@link Roo.data.Store} object.<br>
14799  * <p>
14800  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14801  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14802  * objects.<br>
14803  * <p>
14804  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14805  * @constructor
14806  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14807  * {@link #create}. The parameters are the same.
14808  * @param {Array} data An associative Array of data values keyed by the field name.
14809  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14810  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14811  * not specified an integer id is generated.
14812  */
14813 Roo.data.Record = function(data, id){
14814     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14815     this.data = data;
14816 };
14817
14818 /**
14819  * Generate a constructor for a specific record layout.
14820  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14821  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14822  * Each field definition object may contain the following properties: <ul>
14823  * <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,
14824  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14825  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14826  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14827  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14828  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14829  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14830  * this may be omitted.</p></li>
14831  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14832  * <ul><li>auto (Default, implies no conversion)</li>
14833  * <li>string</li>
14834  * <li>int</li>
14835  * <li>float</li>
14836  * <li>boolean</li>
14837  * <li>date</li></ul></p></li>
14838  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14839  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14840  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14841  * by the Reader into an object that will be stored in the Record. It is passed the
14842  * following parameters:<ul>
14843  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14844  * </ul></p></li>
14845  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14846  * </ul>
14847  * <br>usage:<br><pre><code>
14848 var TopicRecord = Roo.data.Record.create(
14849     {name: 'title', mapping: 'topic_title'},
14850     {name: 'author', mapping: 'username'},
14851     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14852     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14853     {name: 'lastPoster', mapping: 'user2'},
14854     {name: 'excerpt', mapping: 'post_text'}
14855 );
14856
14857 var myNewRecord = new TopicRecord({
14858     title: 'Do my job please',
14859     author: 'noobie',
14860     totalPosts: 1,
14861     lastPost: new Date(),
14862     lastPoster: 'Animal',
14863     excerpt: 'No way dude!'
14864 });
14865 myStore.add(myNewRecord);
14866 </code></pre>
14867  * @method create
14868  * @static
14869  */
14870 Roo.data.Record.create = function(o){
14871     var f = function(){
14872         f.superclass.constructor.apply(this, arguments);
14873     };
14874     Roo.extend(f, Roo.data.Record);
14875     var p = f.prototype;
14876     p.fields = new Roo.util.MixedCollection(false, function(field){
14877         return field.name;
14878     });
14879     for(var i = 0, len = o.length; i < len; i++){
14880         p.fields.add(new Roo.data.Field(o[i]));
14881     }
14882     f.getField = function(name){
14883         return p.fields.get(name);  
14884     };
14885     return f;
14886 };
14887
14888 Roo.data.Record.AUTO_ID = 1000;
14889 Roo.data.Record.EDIT = 'edit';
14890 Roo.data.Record.REJECT = 'reject';
14891 Roo.data.Record.COMMIT = 'commit';
14892
14893 Roo.data.Record.prototype = {
14894     /**
14895      * Readonly flag - true if this record has been modified.
14896      * @type Boolean
14897      */
14898     dirty : false,
14899     editing : false,
14900     error: null,
14901     modified: null,
14902
14903     // private
14904     join : function(store){
14905         this.store = store;
14906     },
14907
14908     /**
14909      * Set the named field to the specified value.
14910      * @param {String} name The name of the field to set.
14911      * @param {Object} value The value to set the field to.
14912      */
14913     set : function(name, value){
14914         if(this.data[name] == value){
14915             return;
14916         }
14917         this.dirty = true;
14918         if(!this.modified){
14919             this.modified = {};
14920         }
14921         if(typeof this.modified[name] == 'undefined'){
14922             this.modified[name] = this.data[name];
14923         }
14924         this.data[name] = value;
14925         if(!this.editing && this.store){
14926             this.store.afterEdit(this);
14927         }       
14928     },
14929
14930     /**
14931      * Get the value of the named field.
14932      * @param {String} name The name of the field to get the value of.
14933      * @return {Object} The value of the field.
14934      */
14935     get : function(name){
14936         return this.data[name]; 
14937     },
14938
14939     // private
14940     beginEdit : function(){
14941         this.editing = true;
14942         this.modified = {}; 
14943     },
14944
14945     // private
14946     cancelEdit : function(){
14947         this.editing = false;
14948         delete this.modified;
14949     },
14950
14951     // private
14952     endEdit : function(){
14953         this.editing = false;
14954         if(this.dirty && this.store){
14955             this.store.afterEdit(this);
14956         }
14957     },
14958
14959     /**
14960      * Usually called by the {@link Roo.data.Store} which owns the Record.
14961      * Rejects all changes made to the Record since either creation, or the last commit operation.
14962      * Modified fields are reverted to their original values.
14963      * <p>
14964      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14965      * of reject operations.
14966      */
14967     reject : function(){
14968         var m = this.modified;
14969         for(var n in m){
14970             if(typeof m[n] != "function"){
14971                 this.data[n] = m[n];
14972             }
14973         }
14974         this.dirty = false;
14975         delete this.modified;
14976         this.editing = false;
14977         if(this.store){
14978             this.store.afterReject(this);
14979         }
14980     },
14981
14982     /**
14983      * Usually called by the {@link Roo.data.Store} which owns the Record.
14984      * Commits all changes made to the Record since either creation, or the last commit operation.
14985      * <p>
14986      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14987      * of commit operations.
14988      */
14989     commit : function(){
14990         this.dirty = false;
14991         delete this.modified;
14992         this.editing = false;
14993         if(this.store){
14994             this.store.afterCommit(this);
14995         }
14996     },
14997
14998     // private
14999     hasError : function(){
15000         return this.error != null;
15001     },
15002
15003     // private
15004     clearError : function(){
15005         this.error = null;
15006     },
15007
15008     /**
15009      * Creates a copy of this record.
15010      * @param {String} id (optional) A new record id if you don't want to use this record's id
15011      * @return {Record}
15012      */
15013     copy : function(newId) {
15014         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15015     }
15016 };/*
15017  * Based on:
15018  * Ext JS Library 1.1.1
15019  * Copyright(c) 2006-2007, Ext JS, LLC.
15020  *
15021  * Originally Released Under LGPL - original licence link has changed is not relivant.
15022  *
15023  * Fork - LGPL
15024  * <script type="text/javascript">
15025  */
15026
15027
15028
15029 /**
15030  * @class Roo.data.Store
15031  * @extends Roo.util.Observable
15032  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15033  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15034  * <p>
15035  * 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
15036  * has no knowledge of the format of the data returned by the Proxy.<br>
15037  * <p>
15038  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15039  * instances from the data object. These records are cached and made available through accessor functions.
15040  * @constructor
15041  * Creates a new Store.
15042  * @param {Object} config A config object containing the objects needed for the Store to access data,
15043  * and read the data into Records.
15044  */
15045 Roo.data.Store = function(config){
15046     this.data = new Roo.util.MixedCollection(false);
15047     this.data.getKey = function(o){
15048         return o.id;
15049     };
15050     this.baseParams = {};
15051     // private
15052     this.paramNames = {
15053         "start" : "start",
15054         "limit" : "limit",
15055         "sort" : "sort",
15056         "dir" : "dir",
15057         "multisort" : "_multisort"
15058     };
15059
15060     if(config && config.data){
15061         this.inlineData = config.data;
15062         delete config.data;
15063     }
15064
15065     Roo.apply(this, config);
15066     
15067     if(this.reader){ // reader passed
15068         this.reader = Roo.factory(this.reader, Roo.data);
15069         this.reader.xmodule = this.xmodule || false;
15070         if(!this.recordType){
15071             this.recordType = this.reader.recordType;
15072         }
15073         if(this.reader.onMetaChange){
15074             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15075         }
15076     }
15077
15078     if(this.recordType){
15079         this.fields = this.recordType.prototype.fields;
15080     }
15081     this.modified = [];
15082
15083     this.addEvents({
15084         /**
15085          * @event datachanged
15086          * Fires when the data cache has changed, and a widget which is using this Store
15087          * as a Record cache should refresh its view.
15088          * @param {Store} this
15089          */
15090         datachanged : true,
15091         /**
15092          * @event metachange
15093          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15094          * @param {Store} this
15095          * @param {Object} meta The JSON metadata
15096          */
15097         metachange : true,
15098         /**
15099          * @event add
15100          * Fires when Records have been added to the Store
15101          * @param {Store} this
15102          * @param {Roo.data.Record[]} records The array of Records added
15103          * @param {Number} index The index at which the record(s) were added
15104          */
15105         add : true,
15106         /**
15107          * @event remove
15108          * Fires when a Record has been removed from the Store
15109          * @param {Store} this
15110          * @param {Roo.data.Record} record The Record that was removed
15111          * @param {Number} index The index at which the record was removed
15112          */
15113         remove : true,
15114         /**
15115          * @event update
15116          * Fires when a Record has been updated
15117          * @param {Store} this
15118          * @param {Roo.data.Record} record The Record that was updated
15119          * @param {String} operation The update operation being performed.  Value may be one of:
15120          * <pre><code>
15121  Roo.data.Record.EDIT
15122  Roo.data.Record.REJECT
15123  Roo.data.Record.COMMIT
15124          * </code></pre>
15125          */
15126         update : true,
15127         /**
15128          * @event clear
15129          * Fires when the data cache has been cleared.
15130          * @param {Store} this
15131          */
15132         clear : true,
15133         /**
15134          * @event beforeload
15135          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15136          * the load action will be canceled.
15137          * @param {Store} this
15138          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15139          */
15140         beforeload : true,
15141         /**
15142          * @event beforeloadadd
15143          * Fires after a new set of Records has been loaded.
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          */
15148         beforeloadadd : true,
15149         /**
15150          * @event load
15151          * Fires after a new set of Records has been loaded, before they are added to the store.
15152          * @param {Store} this
15153          * @param {Roo.data.Record[]} records The Records that were loaded
15154          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15155          * @params {Object} return from reader
15156          */
15157         load : true,
15158         /**
15159          * @event loadexception
15160          * Fires if an exception occurs in the Proxy during loading.
15161          * Called with the signature of the Proxy's "loadexception" event.
15162          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15163          * 
15164          * @param {Proxy} 
15165          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15166          * @param {Object} load options 
15167          * @param {Object} jsonData from your request (normally this contains the Exception)
15168          */
15169         loadexception : true
15170     });
15171     
15172     if(this.proxy){
15173         this.proxy = Roo.factory(this.proxy, Roo.data);
15174         this.proxy.xmodule = this.xmodule || false;
15175         this.relayEvents(this.proxy,  ["loadexception"]);
15176     }
15177     this.sortToggle = {};
15178     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15179
15180     Roo.data.Store.superclass.constructor.call(this);
15181
15182     if(this.inlineData){
15183         this.loadData(this.inlineData);
15184         delete this.inlineData;
15185     }
15186 };
15187
15188 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15189      /**
15190     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15191     * without a remote query - used by combo/forms at present.
15192     */
15193     
15194     /**
15195     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15196     */
15197     /**
15198     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15199     */
15200     /**
15201     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15202     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15203     */
15204     /**
15205     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15206     * on any HTTP request
15207     */
15208     /**
15209     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15210     */
15211     /**
15212     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15213     */
15214     multiSort: false,
15215     /**
15216     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15217     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15218     */
15219     remoteSort : false,
15220
15221     /**
15222     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15223      * loaded or when a record is removed. (defaults to false).
15224     */
15225     pruneModifiedRecords : false,
15226
15227     // private
15228     lastOptions : null,
15229
15230     /**
15231      * Add Records to the Store and fires the add event.
15232      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15233      */
15234     add : function(records){
15235         records = [].concat(records);
15236         for(var i = 0, len = records.length; i < len; i++){
15237             records[i].join(this);
15238         }
15239         var index = this.data.length;
15240         this.data.addAll(records);
15241         this.fireEvent("add", this, records, index);
15242     },
15243
15244     /**
15245      * Remove a Record from the Store and fires the remove event.
15246      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15247      */
15248     remove : function(record){
15249         var index = this.data.indexOf(record);
15250         this.data.removeAt(index);
15251  
15252         if(this.pruneModifiedRecords){
15253             this.modified.remove(record);
15254         }
15255         this.fireEvent("remove", this, record, index);
15256     },
15257
15258     /**
15259      * Remove all Records from the Store and fires the clear event.
15260      */
15261     removeAll : function(){
15262         this.data.clear();
15263         if(this.pruneModifiedRecords){
15264             this.modified = [];
15265         }
15266         this.fireEvent("clear", this);
15267     },
15268
15269     /**
15270      * Inserts Records to the Store at the given index and fires the add event.
15271      * @param {Number} index The start index at which to insert the passed Records.
15272      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15273      */
15274     insert : function(index, records){
15275         records = [].concat(records);
15276         for(var i = 0, len = records.length; i < len; i++){
15277             this.data.insert(index, records[i]);
15278             records[i].join(this);
15279         }
15280         this.fireEvent("add", this, records, index);
15281     },
15282
15283     /**
15284      * Get the index within the cache of the passed Record.
15285      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15286      * @return {Number} The index of the passed Record. Returns -1 if not found.
15287      */
15288     indexOf : function(record){
15289         return this.data.indexOf(record);
15290     },
15291
15292     /**
15293      * Get the index within the cache of the Record with the passed id.
15294      * @param {String} id The id of the Record to find.
15295      * @return {Number} The index of the Record. Returns -1 if not found.
15296      */
15297     indexOfId : function(id){
15298         return this.data.indexOfKey(id);
15299     },
15300
15301     /**
15302      * Get the Record with the specified id.
15303      * @param {String} id The id of the Record to find.
15304      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15305      */
15306     getById : function(id){
15307         return this.data.key(id);
15308     },
15309
15310     /**
15311      * Get the Record at the specified index.
15312      * @param {Number} index The index of the Record to find.
15313      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15314      */
15315     getAt : function(index){
15316         return this.data.itemAt(index);
15317     },
15318
15319     /**
15320      * Returns a range of Records between specified indices.
15321      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15322      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15323      * @return {Roo.data.Record[]} An array of Records
15324      */
15325     getRange : function(start, end){
15326         return this.data.getRange(start, end);
15327     },
15328
15329     // private
15330     storeOptions : function(o){
15331         o = Roo.apply({}, o);
15332         delete o.callback;
15333         delete o.scope;
15334         this.lastOptions = o;
15335     },
15336
15337     /**
15338      * Loads the Record cache from the configured Proxy using the configured Reader.
15339      * <p>
15340      * If using remote paging, then the first load call must specify the <em>start</em>
15341      * and <em>limit</em> properties in the options.params property to establish the initial
15342      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15343      * <p>
15344      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15345      * and this call will return before the new data has been loaded. Perform any post-processing
15346      * in a callback function, or in a "load" event handler.</strong>
15347      * <p>
15348      * @param {Object} options An object containing properties which control loading options:<ul>
15349      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15350      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15351      * passed the following arguments:<ul>
15352      * <li>r : Roo.data.Record[]</li>
15353      * <li>options: Options object from the load call</li>
15354      * <li>success: Boolean success indicator</li></ul></li>
15355      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15356      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15357      * </ul>
15358      */
15359     load : function(options){
15360         options = options || {};
15361         if(this.fireEvent("beforeload", this, options) !== false){
15362             this.storeOptions(options);
15363             var p = Roo.apply(options.params || {}, this.baseParams);
15364             // if meta was not loaded from remote source.. try requesting it.
15365             if (!this.reader.metaFromRemote) {
15366                 p._requestMeta = 1;
15367             }
15368             if(this.sortInfo && this.remoteSort){
15369                 var pn = this.paramNames;
15370                 p[pn["sort"]] = this.sortInfo.field;
15371                 p[pn["dir"]] = this.sortInfo.direction;
15372             }
15373             if (this.multiSort) {
15374                 var pn = this.paramNames;
15375                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15376             }
15377             
15378             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15379         }
15380     },
15381
15382     /**
15383      * Reloads the Record cache from the configured Proxy using the configured Reader and
15384      * the options from the last load operation performed.
15385      * @param {Object} options (optional) An object containing properties which may override the options
15386      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15387      * the most recently used options are reused).
15388      */
15389     reload : function(options){
15390         this.load(Roo.applyIf(options||{}, this.lastOptions));
15391     },
15392
15393     // private
15394     // Called as a callback by the Reader during a load operation.
15395     loadRecords : function(o, options, success){
15396          
15397         if(!o){
15398             if(success !== false){
15399                 this.fireEvent("load", this, [], options, o);
15400             }
15401             if(options.callback){
15402                 options.callback.call(options.scope || this, [], options, false);
15403             }
15404             return;
15405         }
15406         // if data returned failure - throw an exception.
15407         if (o.success === false) {
15408             // show a message if no listener is registered.
15409             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15410                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15411             }
15412             // loadmask wil be hooked into this..
15413             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15414             return;
15415         }
15416         var r = o.records, t = o.totalRecords || r.length;
15417         
15418         this.fireEvent("beforeloadadd", this, r, options, o);
15419         
15420         if(!options || options.add !== true){
15421             if(this.pruneModifiedRecords){
15422                 this.modified = [];
15423             }
15424             for(var i = 0, len = r.length; i < len; i++){
15425                 r[i].join(this);
15426             }
15427             if(this.snapshot){
15428                 this.data = this.snapshot;
15429                 delete this.snapshot;
15430             }
15431             this.data.clear();
15432             this.data.addAll(r);
15433             this.totalLength = t;
15434             this.applySort();
15435             this.fireEvent("datachanged", this);
15436         }else{
15437             this.totalLength = Math.max(t, this.data.length+r.length);
15438             this.add(r);
15439         }
15440         
15441         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15442                 
15443             var e = new Roo.data.Record({});
15444
15445             e.set(this.parent.displayField, this.parent.emptyTitle);
15446             e.set(this.parent.valueField, '');
15447
15448             this.insert(0, e);
15449         }
15450             
15451         this.fireEvent("load", this, r, options, o);
15452         if(options.callback){
15453             options.callback.call(options.scope || this, r, options, true);
15454         }
15455     },
15456
15457
15458     /**
15459      * Loads data from a passed data block. A Reader which understands the format of the data
15460      * must have been configured in the constructor.
15461      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15462      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15463      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15464      */
15465     loadData : function(o, append){
15466         var r = this.reader.readRecords(o);
15467         this.loadRecords(r, {add: append}, true);
15468     },
15469     
15470      /**
15471      * using 'cn' the nested child reader read the child array into it's child stores.
15472      * @param {Object} rec The record with a 'children array
15473      */
15474     loadDataFromChildren : function(rec)
15475     {
15476         this.loadData(this.reader.toLoadData(rec));
15477     },
15478     
15479
15480     /**
15481      * Gets the number of cached records.
15482      * <p>
15483      * <em>If using paging, this may not be the total size of the dataset. If the data object
15484      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15485      * the data set size</em>
15486      */
15487     getCount : function(){
15488         return this.data.length || 0;
15489     },
15490
15491     /**
15492      * Gets the total number of records in the dataset as returned by the server.
15493      * <p>
15494      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15495      * the dataset size</em>
15496      */
15497     getTotalCount : function(){
15498         return this.totalLength || 0;
15499     },
15500
15501     /**
15502      * Returns the sort state of the Store as an object with two properties:
15503      * <pre><code>
15504  field {String} The name of the field by which the Records are sorted
15505  direction {String} The sort order, "ASC" or "DESC"
15506      * </code></pre>
15507      */
15508     getSortState : function(){
15509         return this.sortInfo;
15510     },
15511
15512     // private
15513     applySort : function(){
15514         if(this.sortInfo && !this.remoteSort){
15515             var s = this.sortInfo, f = s.field;
15516             var st = this.fields.get(f).sortType;
15517             var fn = function(r1, r2){
15518                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15519                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15520             };
15521             this.data.sort(s.direction, fn);
15522             if(this.snapshot && this.snapshot != this.data){
15523                 this.snapshot.sort(s.direction, fn);
15524             }
15525         }
15526     },
15527
15528     /**
15529      * Sets the default sort column and order to be used by the next load operation.
15530      * @param {String} fieldName The name of the field to sort by.
15531      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15532      */
15533     setDefaultSort : function(field, dir){
15534         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15535     },
15536
15537     /**
15538      * Sort the Records.
15539      * If remote sorting is used, the sort is performed on the server, and the cache is
15540      * reloaded. If local sorting is used, the cache is sorted internally.
15541      * @param {String} fieldName The name of the field to sort by.
15542      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15543      */
15544     sort : function(fieldName, dir){
15545         var f = this.fields.get(fieldName);
15546         if(!dir){
15547             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15548             
15549             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15550                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15551             }else{
15552                 dir = f.sortDir;
15553             }
15554         }
15555         this.sortToggle[f.name] = dir;
15556         this.sortInfo = {field: f.name, direction: dir};
15557         if(!this.remoteSort){
15558             this.applySort();
15559             this.fireEvent("datachanged", this);
15560         }else{
15561             this.load(this.lastOptions);
15562         }
15563     },
15564
15565     /**
15566      * Calls the specified function for each of the Records in the cache.
15567      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15568      * Returning <em>false</em> aborts and exits the iteration.
15569      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15570      */
15571     each : function(fn, scope){
15572         this.data.each(fn, scope);
15573     },
15574
15575     /**
15576      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15577      * (e.g., during paging).
15578      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15579      */
15580     getModifiedRecords : function(){
15581         return this.modified;
15582     },
15583
15584     // private
15585     createFilterFn : function(property, value, anyMatch){
15586         if(!value.exec){ // not a regex
15587             value = String(value);
15588             if(value.length == 0){
15589                 return false;
15590             }
15591             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15592         }
15593         return function(r){
15594             return value.test(r.data[property]);
15595         };
15596     },
15597
15598     /**
15599      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15600      * @param {String} property A field on your records
15601      * @param {Number} start The record index to start at (defaults to 0)
15602      * @param {Number} end The last record index to include (defaults to length - 1)
15603      * @return {Number} The sum
15604      */
15605     sum : function(property, start, end){
15606         var rs = this.data.items, v = 0;
15607         start = start || 0;
15608         end = (end || end === 0) ? end : rs.length-1;
15609
15610         for(var i = start; i <= end; i++){
15611             v += (rs[i].data[property] || 0);
15612         }
15613         return v;
15614     },
15615
15616     /**
15617      * Filter the records by a specified property.
15618      * @param {String} field A field on your records
15619      * @param {String/RegExp} value Either a string that the field
15620      * should start with or a RegExp to test against the field
15621      * @param {Boolean} anyMatch True to match any part not just the beginning
15622      */
15623     filter : function(property, value, anyMatch){
15624         var fn = this.createFilterFn(property, value, anyMatch);
15625         return fn ? this.filterBy(fn) : this.clearFilter();
15626     },
15627
15628     /**
15629      * Filter by a function. The specified function will be called with each
15630      * record in this data source. If the function returns true the record is included,
15631      * otherwise it is filtered.
15632      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15633      * @param {Object} scope (optional) The scope of the function (defaults to this)
15634      */
15635     filterBy : function(fn, scope){
15636         this.snapshot = this.snapshot || this.data;
15637         this.data = this.queryBy(fn, scope||this);
15638         this.fireEvent("datachanged", this);
15639     },
15640
15641     /**
15642      * Query the records by a specified property.
15643      * @param {String} field A field on your records
15644      * @param {String/RegExp} value Either a string that the field
15645      * should start with or a RegExp to test against the field
15646      * @param {Boolean} anyMatch True to match any part not just the beginning
15647      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15648      */
15649     query : function(property, value, anyMatch){
15650         var fn = this.createFilterFn(property, value, anyMatch);
15651         return fn ? this.queryBy(fn) : this.data.clone();
15652     },
15653
15654     /**
15655      * Query by a function. The specified function will be called with each
15656      * record in this data source. If the function returns true the record is included
15657      * in the results.
15658      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15659      * @param {Object} scope (optional) The scope of the function (defaults to this)
15660       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15661      **/
15662     queryBy : function(fn, scope){
15663         var data = this.snapshot || this.data;
15664         return data.filterBy(fn, scope||this);
15665     },
15666
15667     /**
15668      * Collects unique values for a particular dataIndex from this store.
15669      * @param {String} dataIndex The property to collect
15670      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15671      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15672      * @return {Array} An array of the unique values
15673      **/
15674     collect : function(dataIndex, allowNull, bypassFilter){
15675         var d = (bypassFilter === true && this.snapshot) ?
15676                 this.snapshot.items : this.data.items;
15677         var v, sv, r = [], l = {};
15678         for(var i = 0, len = d.length; i < len; i++){
15679             v = d[i].data[dataIndex];
15680             sv = String(v);
15681             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15682                 l[sv] = true;
15683                 r[r.length] = v;
15684             }
15685         }
15686         return r;
15687     },
15688
15689     /**
15690      * Revert to a view of the Record cache with no filtering applied.
15691      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15692      */
15693     clearFilter : function(suppressEvent){
15694         if(this.snapshot && this.snapshot != this.data){
15695             this.data = this.snapshot;
15696             delete this.snapshot;
15697             if(suppressEvent !== true){
15698                 this.fireEvent("datachanged", this);
15699             }
15700         }
15701     },
15702
15703     // private
15704     afterEdit : function(record){
15705         if(this.modified.indexOf(record) == -1){
15706             this.modified.push(record);
15707         }
15708         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15709     },
15710     
15711     // private
15712     afterReject : function(record){
15713         this.modified.remove(record);
15714         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15715     },
15716
15717     // private
15718     afterCommit : function(record){
15719         this.modified.remove(record);
15720         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15721     },
15722
15723     /**
15724      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15725      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15726      */
15727     commitChanges : function(){
15728         var m = this.modified.slice(0);
15729         this.modified = [];
15730         for(var i = 0, len = m.length; i < len; i++){
15731             m[i].commit();
15732         }
15733     },
15734
15735     /**
15736      * Cancel outstanding changes on all changed records.
15737      */
15738     rejectChanges : function(){
15739         var m = this.modified.slice(0);
15740         this.modified = [];
15741         for(var i = 0, len = m.length; i < len; i++){
15742             m[i].reject();
15743         }
15744     },
15745
15746     onMetaChange : function(meta, rtype, o){
15747         this.recordType = rtype;
15748         this.fields = rtype.prototype.fields;
15749         delete this.snapshot;
15750         this.sortInfo = meta.sortInfo || this.sortInfo;
15751         this.modified = [];
15752         this.fireEvent('metachange', this, this.reader.meta);
15753     },
15754     
15755     moveIndex : function(data, type)
15756     {
15757         var index = this.indexOf(data);
15758         
15759         var newIndex = index + type;
15760         
15761         this.remove(data);
15762         
15763         this.insert(newIndex, data);
15764         
15765     }
15766 });/*
15767  * Based on:
15768  * Ext JS Library 1.1.1
15769  * Copyright(c) 2006-2007, Ext JS, LLC.
15770  *
15771  * Originally Released Under LGPL - original licence link has changed is not relivant.
15772  *
15773  * Fork - LGPL
15774  * <script type="text/javascript">
15775  */
15776
15777 /**
15778  * @class Roo.data.SimpleStore
15779  * @extends Roo.data.Store
15780  * Small helper class to make creating Stores from Array data easier.
15781  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15782  * @cfg {Array} fields An array of field definition objects, or field name strings.
15783  * @cfg {Object} an existing reader (eg. copied from another store)
15784  * @cfg {Array} data The multi-dimensional array of data
15785  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15786  * @cfg {Roo.data.Reader} reader  [not-required] 
15787  * @constructor
15788  * @param {Object} config
15789  */
15790 Roo.data.SimpleStore = function(config)
15791 {
15792     Roo.data.SimpleStore.superclass.constructor.call(this, {
15793         isLocal : true,
15794         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15795                 id: config.id
15796             },
15797             Roo.data.Record.create(config.fields)
15798         ),
15799         proxy : new Roo.data.MemoryProxy(config.data)
15800     });
15801     this.load();
15802 };
15803 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15804  * Based on:
15805  * Ext JS Library 1.1.1
15806  * Copyright(c) 2006-2007, Ext JS, LLC.
15807  *
15808  * Originally Released Under LGPL - original licence link has changed is not relivant.
15809  *
15810  * Fork - LGPL
15811  * <script type="text/javascript">
15812  */
15813
15814 /**
15815 /**
15816  * @extends Roo.data.Store
15817  * @class Roo.data.JsonStore
15818  * Small helper class to make creating Stores for JSON data easier. <br/>
15819 <pre><code>
15820 var store = new Roo.data.JsonStore({
15821     url: 'get-images.php',
15822     root: 'images',
15823     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15824 });
15825 </code></pre>
15826  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15827  * JsonReader and HttpProxy (unless inline data is provided).</b>
15828  * @cfg {Array} fields An array of field definition objects, or field name strings.
15829  * @constructor
15830  * @param {Object} config
15831  */
15832 Roo.data.JsonStore = function(c){
15833     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15834         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15835         reader: new Roo.data.JsonReader(c, c.fields)
15836     }));
15837 };
15838 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15839  * Based on:
15840  * Ext JS Library 1.1.1
15841  * Copyright(c) 2006-2007, Ext JS, LLC.
15842  *
15843  * Originally Released Under LGPL - original licence link has changed is not relivant.
15844  *
15845  * Fork - LGPL
15846  * <script type="text/javascript">
15847  */
15848
15849  
15850 Roo.data.Field = function(config){
15851     if(typeof config == "string"){
15852         config = {name: config};
15853     }
15854     Roo.apply(this, config);
15855     
15856     if(!this.type){
15857         this.type = "auto";
15858     }
15859     
15860     var st = Roo.data.SortTypes;
15861     // named sortTypes are supported, here we look them up
15862     if(typeof this.sortType == "string"){
15863         this.sortType = st[this.sortType];
15864     }
15865     
15866     // set default sortType for strings and dates
15867     if(!this.sortType){
15868         switch(this.type){
15869             case "string":
15870                 this.sortType = st.asUCString;
15871                 break;
15872             case "date":
15873                 this.sortType = st.asDate;
15874                 break;
15875             default:
15876                 this.sortType = st.none;
15877         }
15878     }
15879
15880     // define once
15881     var stripRe = /[\$,%]/g;
15882
15883     // prebuilt conversion function for this field, instead of
15884     // switching every time we're reading a value
15885     if(!this.convert){
15886         var cv, dateFormat = this.dateFormat;
15887         switch(this.type){
15888             case "":
15889             case "auto":
15890             case undefined:
15891                 cv = function(v){ return v; };
15892                 break;
15893             case "string":
15894                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15895                 break;
15896             case "int":
15897                 cv = function(v){
15898                     return v !== undefined && v !== null && v !== '' ?
15899                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15900                     };
15901                 break;
15902             case "float":
15903                 cv = function(v){
15904                     return v !== undefined && v !== null && v !== '' ?
15905                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15906                     };
15907                 break;
15908             case "bool":
15909             case "boolean":
15910                 cv = function(v){ return v === true || v === "true" || v == 1; };
15911                 break;
15912             case "date":
15913                 cv = function(v){
15914                     if(!v){
15915                         return '';
15916                     }
15917                     if(v instanceof Date){
15918                         return v;
15919                     }
15920                     if(dateFormat){
15921                         if(dateFormat == "timestamp"){
15922                             return new Date(v*1000);
15923                         }
15924                         return Date.parseDate(v, dateFormat);
15925                     }
15926                     var parsed = Date.parse(v);
15927                     return parsed ? new Date(parsed) : null;
15928                 };
15929              break;
15930             
15931         }
15932         this.convert = cv;
15933     }
15934 };
15935
15936 Roo.data.Field.prototype = {
15937     dateFormat: null,
15938     defaultValue: "",
15939     mapping: null,
15940     sortType : null,
15941     sortDir : "ASC"
15942 };/*
15943  * Based on:
15944  * Ext JS Library 1.1.1
15945  * Copyright(c) 2006-2007, Ext JS, LLC.
15946  *
15947  * Originally Released Under LGPL - original licence link has changed is not relivant.
15948  *
15949  * Fork - LGPL
15950  * <script type="text/javascript">
15951  */
15952  
15953 // Base class for reading structured data from a data source.  This class is intended to be
15954 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15955
15956 /**
15957  * @class Roo.data.DataReader
15958  * @abstract
15959  * Base class for reading structured data from a data source.  This class is intended to be
15960  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15961  */
15962
15963 Roo.data.DataReader = function(meta, recordType){
15964     
15965     this.meta = meta;
15966     
15967     this.recordType = recordType instanceof Array ? 
15968         Roo.data.Record.create(recordType) : recordType;
15969 };
15970
15971 Roo.data.DataReader.prototype = {
15972     
15973     
15974     readerType : 'Data',
15975      /**
15976      * Create an empty record
15977      * @param {Object} data (optional) - overlay some values
15978      * @return {Roo.data.Record} record created.
15979      */
15980     newRow :  function(d) {
15981         var da =  {};
15982         this.recordType.prototype.fields.each(function(c) {
15983             switch( c.type) {
15984                 case 'int' : da[c.name] = 0; break;
15985                 case 'date' : da[c.name] = new Date(); break;
15986                 case 'float' : da[c.name] = 0.0; break;
15987                 case 'boolean' : da[c.name] = false; break;
15988                 default : da[c.name] = ""; break;
15989             }
15990             
15991         });
15992         return new this.recordType(Roo.apply(da, d));
15993     }
15994     
15995     
15996 };/*
15997  * Based on:
15998  * Ext JS Library 1.1.1
15999  * Copyright(c) 2006-2007, Ext JS, LLC.
16000  *
16001  * Originally Released Under LGPL - original licence link has changed is not relivant.
16002  *
16003  * Fork - LGPL
16004  * <script type="text/javascript">
16005  */
16006
16007 /**
16008  * @class Roo.data.DataProxy
16009  * @extends Roo.util.Observable
16010  * @abstract
16011  * This class is an abstract base class for implementations which provide retrieval of
16012  * unformatted data objects.<br>
16013  * <p>
16014  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16015  * (of the appropriate type which knows how to parse the data object) to provide a block of
16016  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16017  * <p>
16018  * Custom implementations must implement the load method as described in
16019  * {@link Roo.data.HttpProxy#load}.
16020  */
16021 Roo.data.DataProxy = function(){
16022     this.addEvents({
16023         /**
16024          * @event beforeload
16025          * Fires before a network request is made to retrieve a data object.
16026          * @param {Object} This DataProxy object.
16027          * @param {Object} params The params parameter to the load function.
16028          */
16029         beforeload : true,
16030         /**
16031          * @event load
16032          * Fires before the load method's callback is called.
16033          * @param {Object} This DataProxy object.
16034          * @param {Object} o The data object.
16035          * @param {Object} arg The callback argument object passed to the load function.
16036          */
16037         load : true,
16038         /**
16039          * @event loadexception
16040          * Fires if an Exception occurs during data retrieval.
16041          * @param {Object} This DataProxy object.
16042          * @param {Object} o The data object.
16043          * @param {Object} arg The callback argument object passed to the load function.
16044          * @param {Object} e The Exception.
16045          */
16046         loadexception : true
16047     });
16048     Roo.data.DataProxy.superclass.constructor.call(this);
16049 };
16050
16051 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16052
16053     /**
16054      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16055      */
16056 /*
16057  * Based on:
16058  * Ext JS Library 1.1.1
16059  * Copyright(c) 2006-2007, Ext JS, LLC.
16060  *
16061  * Originally Released Under LGPL - original licence link has changed is not relivant.
16062  *
16063  * Fork - LGPL
16064  * <script type="text/javascript">
16065  */
16066 /**
16067  * @class Roo.data.MemoryProxy
16068  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16069  * to the Reader when its load method is called.
16070  * @constructor
16071  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16072  */
16073 Roo.data.MemoryProxy = function(data){
16074     if (data.data) {
16075         data = data.data;
16076     }
16077     Roo.data.MemoryProxy.superclass.constructor.call(this);
16078     this.data = data;
16079 };
16080
16081 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16082     
16083     /**
16084      * Load data from the requested source (in this case an in-memory
16085      * data object passed to the constructor), read the data object into
16086      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16087      * process that block using the passed callback.
16088      * @param {Object} params This parameter is not used by the MemoryProxy class.
16089      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16090      * object into a block of Roo.data.Records.
16091      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16092      * The function must be passed <ul>
16093      * <li>The Record block object</li>
16094      * <li>The "arg" argument from the load function</li>
16095      * <li>A boolean success indicator</li>
16096      * </ul>
16097      * @param {Object} scope The scope in which to call the callback
16098      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16099      */
16100     load : function(params, reader, callback, scope, arg){
16101         params = params || {};
16102         var result;
16103         try {
16104             result = reader.readRecords(params.data ? params.data :this.data);
16105         }catch(e){
16106             this.fireEvent("loadexception", this, arg, null, e);
16107             callback.call(scope, null, arg, false);
16108             return;
16109         }
16110         callback.call(scope, result, arg, true);
16111     },
16112     
16113     // private
16114     update : function(params, records){
16115         
16116     }
16117 });/*
16118  * Based on:
16119  * Ext JS Library 1.1.1
16120  * Copyright(c) 2006-2007, Ext JS, LLC.
16121  *
16122  * Originally Released Under LGPL - original licence link has changed is not relivant.
16123  *
16124  * Fork - LGPL
16125  * <script type="text/javascript">
16126  */
16127 /**
16128  * @class Roo.data.HttpProxy
16129  * @extends Roo.data.DataProxy
16130  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16131  * configured to reference a certain URL.<br><br>
16132  * <p>
16133  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16134  * from which the running page was served.<br><br>
16135  * <p>
16136  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16137  * <p>
16138  * Be aware that to enable the browser to parse an XML document, the server must set
16139  * the Content-Type header in the HTTP response to "text/xml".
16140  * @constructor
16141  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16142  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16143  * will be used to make the request.
16144  */
16145 Roo.data.HttpProxy = function(conn){
16146     Roo.data.HttpProxy.superclass.constructor.call(this);
16147     // is conn a conn config or a real conn?
16148     this.conn = conn;
16149     this.useAjax = !conn || !conn.events;
16150   
16151 };
16152
16153 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16154     // thse are take from connection...
16155     
16156     /**
16157      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16158      */
16159     /**
16160      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16161      * extra parameters to each request made by this object. (defaults to undefined)
16162      */
16163     /**
16164      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16165      *  to each request made by this object. (defaults to undefined)
16166      */
16167     /**
16168      * @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)
16169      */
16170     /**
16171      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16172      */
16173      /**
16174      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16175      * @type Boolean
16176      */
16177   
16178
16179     /**
16180      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16181      * @type Boolean
16182      */
16183     /**
16184      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16185      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16186      * a finer-grained basis than the DataProxy events.
16187      */
16188     getConnection : function(){
16189         return this.useAjax ? Roo.Ajax : this.conn;
16190     },
16191
16192     /**
16193      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16194      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16195      * process that block using the passed callback.
16196      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16197      * for the request to the remote server.
16198      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16199      * object into a block of Roo.data.Records.
16200      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16201      * The function must be passed <ul>
16202      * <li>The Record block object</li>
16203      * <li>The "arg" argument from the load function</li>
16204      * <li>A boolean success indicator</li>
16205      * </ul>
16206      * @param {Object} scope The scope in which to call the callback
16207      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16208      */
16209     load : function(params, reader, callback, scope, arg){
16210         if(this.fireEvent("beforeload", this, params) !== false){
16211             var  o = {
16212                 params : params || {},
16213                 request: {
16214                     callback : callback,
16215                     scope : scope,
16216                     arg : arg
16217                 },
16218                 reader: reader,
16219                 callback : this.loadResponse,
16220                 scope: this
16221             };
16222             if(this.useAjax){
16223                 Roo.applyIf(o, this.conn);
16224                 if(this.activeRequest){
16225                     Roo.Ajax.abort(this.activeRequest);
16226                 }
16227                 this.activeRequest = Roo.Ajax.request(o);
16228             }else{
16229                 this.conn.request(o);
16230             }
16231         }else{
16232             callback.call(scope||this, null, arg, false);
16233         }
16234     },
16235
16236     // private
16237     loadResponse : function(o, success, response){
16238         delete this.activeRequest;
16239         if(!success){
16240             this.fireEvent("loadexception", this, o, response);
16241             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16242             return;
16243         }
16244         var result;
16245         try {
16246             result = o.reader.read(response);
16247         }catch(e){
16248             this.fireEvent("loadexception", this, o, response, e);
16249             o.request.callback.call(o.request.scope, {
16250                     success : false,
16251                     raw : {
16252                         errorMsg : response.responseText
16253                     }
16254                     
16255                 }, o.request.arg, false);
16256             return;
16257         }
16258         
16259         this.fireEvent("load", this, o, o.request.arg);
16260         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16261     },
16262
16263     // private
16264     update : function(dataSet){
16265
16266     },
16267
16268     // private
16269     updateResponse : function(dataSet){
16270
16271     }
16272 });/*
16273  * Based on:
16274  * Ext JS Library 1.1.1
16275  * Copyright(c) 2006-2007, Ext JS, LLC.
16276  *
16277  * Originally Released Under LGPL - original licence link has changed is not relivant.
16278  *
16279  * Fork - LGPL
16280  * <script type="text/javascript">
16281  */
16282
16283 /**
16284  * @class Roo.data.ScriptTagProxy
16285  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16286  * other than the originating domain of the running page.<br><br>
16287  * <p>
16288  * <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
16289  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16290  * <p>
16291  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16292  * source code that is used as the source inside a &lt;script> tag.<br><br>
16293  * <p>
16294  * In order for the browser to process the returned data, the server must wrap the data object
16295  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16296  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16297  * depending on whether the callback name was passed:
16298  * <p>
16299  * <pre><code>
16300 boolean scriptTag = false;
16301 String cb = request.getParameter("callback");
16302 if (cb != null) {
16303     scriptTag = true;
16304     response.setContentType("text/javascript");
16305 } else {
16306     response.setContentType("application/x-json");
16307 }
16308 Writer out = response.getWriter();
16309 if (scriptTag) {
16310     out.write(cb + "(");
16311 }
16312 out.print(dataBlock.toJsonString());
16313 if (scriptTag) {
16314     out.write(");");
16315 }
16316 </pre></code>
16317  *
16318  * @constructor
16319  * @param {Object} config A configuration object.
16320  */
16321 Roo.data.ScriptTagProxy = function(config){
16322     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16323     Roo.apply(this, config);
16324     this.head = document.getElementsByTagName("head")[0];
16325 };
16326
16327 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16328
16329 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16330     /**
16331      * @cfg {String} url The URL from which to request the data object.
16332      */
16333     /**
16334      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16335      */
16336     timeout : 30000,
16337     /**
16338      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16339      * the server the name of the callback function set up by the load call to process the returned data object.
16340      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16341      * javascript output which calls this named function passing the data object as its only parameter.
16342      */
16343     callbackParam : "callback",
16344     /**
16345      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16346      * name to the request.
16347      */
16348     nocache : true,
16349
16350     /**
16351      * Load data from the configured URL, read the data object into
16352      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16353      * process that block using the passed callback.
16354      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16355      * for the request to the remote server.
16356      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16357      * object into a block of Roo.data.Records.
16358      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16359      * The function must be passed <ul>
16360      * <li>The Record block object</li>
16361      * <li>The "arg" argument from the load function</li>
16362      * <li>A boolean success indicator</li>
16363      * </ul>
16364      * @param {Object} scope The scope in which to call the callback
16365      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16366      */
16367     load : function(params, reader, callback, scope, arg){
16368         if(this.fireEvent("beforeload", this, params) !== false){
16369
16370             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16371
16372             var url = this.url;
16373             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16374             if(this.nocache){
16375                 url += "&_dc=" + (new Date().getTime());
16376             }
16377             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16378             var trans = {
16379                 id : transId,
16380                 cb : "stcCallback"+transId,
16381                 scriptId : "stcScript"+transId,
16382                 params : params,
16383                 arg : arg,
16384                 url : url,
16385                 callback : callback,
16386                 scope : scope,
16387                 reader : reader
16388             };
16389             var conn = this;
16390
16391             window[trans.cb] = function(o){
16392                 conn.handleResponse(o, trans);
16393             };
16394
16395             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16396
16397             if(this.autoAbort !== false){
16398                 this.abort();
16399             }
16400
16401             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16402
16403             var script = document.createElement("script");
16404             script.setAttribute("src", url);
16405             script.setAttribute("type", "text/javascript");
16406             script.setAttribute("id", trans.scriptId);
16407             this.head.appendChild(script);
16408
16409             this.trans = trans;
16410         }else{
16411             callback.call(scope||this, null, arg, false);
16412         }
16413     },
16414
16415     // private
16416     isLoading : function(){
16417         return this.trans ? true : false;
16418     },
16419
16420     /**
16421      * Abort the current server request.
16422      */
16423     abort : function(){
16424         if(this.isLoading()){
16425             this.destroyTrans(this.trans);
16426         }
16427     },
16428
16429     // private
16430     destroyTrans : function(trans, isLoaded){
16431         this.head.removeChild(document.getElementById(trans.scriptId));
16432         clearTimeout(trans.timeoutId);
16433         if(isLoaded){
16434             window[trans.cb] = undefined;
16435             try{
16436                 delete window[trans.cb];
16437             }catch(e){}
16438         }else{
16439             // if hasn't been loaded, wait for load to remove it to prevent script error
16440             window[trans.cb] = function(){
16441                 window[trans.cb] = undefined;
16442                 try{
16443                     delete window[trans.cb];
16444                 }catch(e){}
16445             };
16446         }
16447     },
16448
16449     // private
16450     handleResponse : function(o, trans){
16451         this.trans = false;
16452         this.destroyTrans(trans, true);
16453         var result;
16454         try {
16455             result = trans.reader.readRecords(o);
16456         }catch(e){
16457             this.fireEvent("loadexception", this, o, trans.arg, e);
16458             trans.callback.call(trans.scope||window, null, trans.arg, false);
16459             return;
16460         }
16461         this.fireEvent("load", this, o, trans.arg);
16462         trans.callback.call(trans.scope||window, result, trans.arg, true);
16463     },
16464
16465     // private
16466     handleFailure : function(trans){
16467         this.trans = false;
16468         this.destroyTrans(trans, false);
16469         this.fireEvent("loadexception", this, null, trans.arg);
16470         trans.callback.call(trans.scope||window, null, trans.arg, false);
16471     }
16472 });/*
16473  * Based on:
16474  * Ext JS Library 1.1.1
16475  * Copyright(c) 2006-2007, Ext JS, LLC.
16476  *
16477  * Originally Released Under LGPL - original licence link has changed is not relivant.
16478  *
16479  * Fork - LGPL
16480  * <script type="text/javascript">
16481  */
16482
16483 /**
16484  * @class Roo.data.JsonReader
16485  * @extends Roo.data.DataReader
16486  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16487  * based on mappings in a provided Roo.data.Record constructor.
16488  * 
16489  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16490  * in the reply previously. 
16491  * 
16492  * <p>
16493  * Example code:
16494  * <pre><code>
16495 var RecordDef = Roo.data.Record.create([
16496     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16497     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16498 ]);
16499 var myReader = new Roo.data.JsonReader({
16500     totalProperty: "results",    // The property which contains the total dataset size (optional)
16501     root: "rows",                // The property which contains an Array of row objects
16502     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16503 }, RecordDef);
16504 </code></pre>
16505  * <p>
16506  * This would consume a JSON file like this:
16507  * <pre><code>
16508 { 'results': 2, 'rows': [
16509     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16510     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16511 }
16512 </code></pre>
16513  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16514  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16515  * paged from the remote server.
16516  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16517  * @cfg {String} root name of the property which contains the Array of row objects.
16518  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16519  * @cfg {Array} fields Array of field definition objects
16520  * @constructor
16521  * Create a new JsonReader
16522  * @param {Object} meta Metadata configuration options
16523  * @param {Object} recordType Either an Array of field definition objects,
16524  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16525  */
16526 Roo.data.JsonReader = function(meta, recordType){
16527     
16528     meta = meta || {};
16529     // set some defaults:
16530     Roo.applyIf(meta, {
16531         totalProperty: 'total',
16532         successProperty : 'success',
16533         root : 'data',
16534         id : 'id'
16535     });
16536     
16537     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16538 };
16539 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16540     
16541     readerType : 'Json',
16542     
16543     /**
16544      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16545      * Used by Store query builder to append _requestMeta to params.
16546      * 
16547      */
16548     metaFromRemote : false,
16549     /**
16550      * This method is only used by a DataProxy which has retrieved data from a remote server.
16551      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16552      * @return {Object} data A data block which is used by an Roo.data.Store object as
16553      * a cache of Roo.data.Records.
16554      */
16555     read : function(response){
16556         var json = response.responseText;
16557        
16558         var o = /* eval:var:o */ eval("("+json+")");
16559         if(!o) {
16560             throw {message: "JsonReader.read: Json object not found"};
16561         }
16562         
16563         if(o.metaData){
16564             
16565             delete this.ef;
16566             this.metaFromRemote = true;
16567             this.meta = o.metaData;
16568             this.recordType = Roo.data.Record.create(o.metaData.fields);
16569             this.onMetaChange(this.meta, this.recordType, o);
16570         }
16571         return this.readRecords(o);
16572     },
16573
16574     // private function a store will implement
16575     onMetaChange : function(meta, recordType, o){
16576
16577     },
16578
16579     /**
16580          * @ignore
16581          */
16582     simpleAccess: function(obj, subsc) {
16583         return obj[subsc];
16584     },
16585
16586         /**
16587          * @ignore
16588          */
16589     getJsonAccessor: function(){
16590         var re = /[\[\.]/;
16591         return function(expr) {
16592             try {
16593                 return(re.test(expr))
16594                     ? new Function("obj", "return obj." + expr)
16595                     : function(obj){
16596                         return obj[expr];
16597                     };
16598             } catch(e){}
16599             return Roo.emptyFn;
16600         };
16601     }(),
16602
16603     /**
16604      * Create a data block containing Roo.data.Records from an XML document.
16605      * @param {Object} o An object which contains an Array of row objects in the property specified
16606      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16607      * which contains the total size of the dataset.
16608      * @return {Object} data A data block which is used by an Roo.data.Store object as
16609      * a cache of Roo.data.Records.
16610      */
16611     readRecords : function(o){
16612         /**
16613          * After any data loads, the raw JSON data is available for further custom processing.
16614          * @type Object
16615          */
16616         this.o = o;
16617         var s = this.meta, Record = this.recordType,
16618             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16619
16620 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16621         if (!this.ef) {
16622             if(s.totalProperty) {
16623                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16624                 }
16625                 if(s.successProperty) {
16626                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16627                 }
16628                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16629                 if (s.id) {
16630                         var g = this.getJsonAccessor(s.id);
16631                         this.getId = function(rec) {
16632                                 var r = g(rec);  
16633                                 return (r === undefined || r === "") ? null : r;
16634                         };
16635                 } else {
16636                         this.getId = function(){return null;};
16637                 }
16638             this.ef = [];
16639             for(var jj = 0; jj < fl; jj++){
16640                 f = fi[jj];
16641                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16642                 this.ef[jj] = this.getJsonAccessor(map);
16643             }
16644         }
16645
16646         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16647         if(s.totalProperty){
16648             var vt = parseInt(this.getTotal(o), 10);
16649             if(!isNaN(vt)){
16650                 totalRecords = vt;
16651             }
16652         }
16653         if(s.successProperty){
16654             var vs = this.getSuccess(o);
16655             if(vs === false || vs === 'false'){
16656                 success = false;
16657             }
16658         }
16659         var records = [];
16660         for(var i = 0; i < c; i++){
16661                 var n = root[i];
16662             var values = {};
16663             var id = this.getId(n);
16664             for(var j = 0; j < fl; j++){
16665                 f = fi[j];
16666             var v = this.ef[j](n);
16667             if (!f.convert) {
16668                 Roo.log('missing convert for ' + f.name);
16669                 Roo.log(f);
16670                 continue;
16671             }
16672             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16673             }
16674             var record = new Record(values, id);
16675             record.json = n;
16676             records[i] = record;
16677         }
16678         return {
16679             raw : o,
16680             success : success,
16681             records : records,
16682             totalRecords : totalRecords
16683         };
16684     },
16685     // used when loading children.. @see loadDataFromChildren
16686     toLoadData: function(rec)
16687     {
16688         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16689         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16690         return { data : data, total : data.length };
16691         
16692     }
16693 });/*
16694  * Based on:
16695  * Ext JS Library 1.1.1
16696  * Copyright(c) 2006-2007, Ext JS, LLC.
16697  *
16698  * Originally Released Under LGPL - original licence link has changed is not relivant.
16699  *
16700  * Fork - LGPL
16701  * <script type="text/javascript">
16702  */
16703
16704 /**
16705  * @class Roo.data.ArrayReader
16706  * @extends Roo.data.DataReader
16707  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16708  * Each element of that Array represents a row of data fields. The
16709  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16710  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16711  * <p>
16712  * Example code:.
16713  * <pre><code>
16714 var RecordDef = Roo.data.Record.create([
16715     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16716     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16717 ]);
16718 var myReader = new Roo.data.ArrayReader({
16719     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16720 }, RecordDef);
16721 </code></pre>
16722  * <p>
16723  * This would consume an Array like this:
16724  * <pre><code>
16725 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16726   </code></pre>
16727  
16728  * @constructor
16729  * Create a new JsonReader
16730  * @param {Object} meta Metadata configuration options.
16731  * @param {Object|Array} recordType Either an Array of field definition objects
16732  * 
16733  * @cfg {Array} fields Array of field definition objects
16734  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16735  * as specified to {@link Roo.data.Record#create},
16736  * or an {@link Roo.data.Record} object
16737  *
16738  * 
16739  * created using {@link Roo.data.Record#create}.
16740  */
16741 Roo.data.ArrayReader = function(meta, recordType)
16742 {    
16743     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16744 };
16745
16746 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16747     
16748       /**
16749      * Create a data block containing Roo.data.Records from an XML document.
16750      * @param {Object} o An Array of row objects which represents the dataset.
16751      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16752      * a cache of Roo.data.Records.
16753      */
16754     readRecords : function(o)
16755     {
16756         var sid = this.meta ? this.meta.id : null;
16757         var recordType = this.recordType, fields = recordType.prototype.fields;
16758         var records = [];
16759         var root = o;
16760         for(var i = 0; i < root.length; i++){
16761             var n = root[i];
16762             var values = {};
16763             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16764             for(var j = 0, jlen = fields.length; j < jlen; j++){
16765                 var f = fields.items[j];
16766                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16767                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16768                 v = f.convert(v);
16769                 values[f.name] = v;
16770             }
16771             var record = new recordType(values, id);
16772             record.json = n;
16773             records[records.length] = record;
16774         }
16775         return {
16776             records : records,
16777             totalRecords : records.length
16778         };
16779     },
16780     // used when loading children.. @see loadDataFromChildren
16781     toLoadData: function(rec)
16782     {
16783         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16784         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16785         
16786     }
16787     
16788     
16789 });/*
16790  * - LGPL
16791  * * 
16792  */
16793
16794 /**
16795  * @class Roo.bootstrap.form.ComboBox
16796  * @extends Roo.bootstrap.form.TriggerField
16797  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16798  * @cfg {Boolean} append (true|false) default false
16799  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16800  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16801  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16802  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16803  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16804  * @cfg {Boolean} animate default true
16805  * @cfg {Boolean} emptyResultText only for touch device
16806  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16807  * @cfg {String} emptyTitle default ''
16808  * @cfg {Number} width fixed with? experimental
16809  * @constructor
16810  * Create a new ComboBox.
16811  * @param {Object} config Configuration options
16812  */
16813 Roo.bootstrap.form.ComboBox = function(config){
16814     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16815     this.addEvents({
16816         /**
16817          * @event expand
16818          * Fires when the dropdown list is expanded
16819         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16820         */
16821         'expand' : true,
16822         /**
16823          * @event collapse
16824          * Fires when the dropdown list is collapsed
16825         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16826         */
16827         'collapse' : true,
16828         /**
16829          * @event beforeselect
16830          * Fires before a list item is selected. Return false to cancel the selection.
16831         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16832         * @param {Roo.data.Record} record The data record returned from the underlying store
16833         * @param {Number} index The index of the selected item in the dropdown list
16834         */
16835         'beforeselect' : true,
16836         /**
16837          * @event select
16838          * Fires when a list item is selected
16839         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16840         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16841         * @param {Number} index The index of the selected item in the dropdown list
16842         */
16843         'select' : true,
16844         /**
16845          * @event beforequery
16846          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16847          * The event object passed has these properties:
16848         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16849         * @param {String} query The query
16850         * @param {Boolean} forceAll true to force "all" query
16851         * @param {Boolean} cancel true to cancel the query
16852         * @param {Object} e The query event object
16853         */
16854         'beforequery': true,
16855          /**
16856          * @event add
16857          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16858         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16859         */
16860         'add' : true,
16861         /**
16862          * @event edit
16863          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16864         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16865         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16866         */
16867         'edit' : true,
16868         /**
16869          * @event remove
16870          * Fires when the remove value from the combobox array
16871         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16872         */
16873         'remove' : true,
16874         /**
16875          * @event afterremove
16876          * Fires when the remove value from the combobox array
16877         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16878         */
16879         'afterremove' : true,
16880         /**
16881          * @event specialfilter
16882          * Fires when specialfilter
16883             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16884             */
16885         'specialfilter' : true,
16886         /**
16887          * @event tick
16888          * Fires when tick the element
16889             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16890             */
16891         'tick' : true,
16892         /**
16893          * @event touchviewdisplay
16894          * Fires when touch view require special display (default is using displayField)
16895             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16896             * @param {Object} cfg set html .
16897             */
16898         'touchviewdisplay' : true
16899         
16900     });
16901     
16902     this.item = [];
16903     this.tickItems = [];
16904     
16905     this.selectedIndex = -1;
16906     if(this.mode == 'local'){
16907         if(config.queryDelay === undefined){
16908             this.queryDelay = 10;
16909         }
16910         if(config.minChars === undefined){
16911             this.minChars = 0;
16912         }
16913     }
16914 };
16915
16916 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16917      
16918     /**
16919      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16920      * rendering into an Roo.Editor, defaults to false)
16921      */
16922     /**
16923      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16924      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16925      */
16926     /**
16927      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16928      */
16929     /**
16930      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16931      * the dropdown list (defaults to undefined, with no header element)
16932      */
16933
16934      /**
16935      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16936      */
16937      
16938      /**
16939      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16940      */
16941     listWidth: undefined,
16942     /**
16943      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16944      * mode = 'remote' or 'text' if mode = 'local')
16945      */
16946     displayField: undefined,
16947     
16948     /**
16949      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16950      * mode = 'remote' or 'value' if mode = 'local'). 
16951      * Note: use of a valueField requires the user make a selection
16952      * in order for a value to be mapped.
16953      */
16954     valueField: undefined,
16955     /**
16956      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16957      */
16958     modalTitle : '',
16959     
16960     /**
16961      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16962      * field's data value (defaults to the underlying DOM element's name)
16963      */
16964     hiddenName: undefined,
16965     /**
16966      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16967      */
16968     listClass: '',
16969     /**
16970      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16971      */
16972     selectedClass: 'active',
16973     
16974     /**
16975      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16976      */
16977     shadow:'sides',
16978     /**
16979      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16980      * anchor positions (defaults to 'tl-bl')
16981      */
16982     listAlign: 'tl-bl?',
16983     /**
16984      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16985      */
16986     maxHeight: 300,
16987     /**
16988      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16989      * query specified by the allQuery config option (defaults to 'query')
16990      */
16991     triggerAction: 'query',
16992     /**
16993      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16994      * (defaults to 4, does not apply if editable = false)
16995      */
16996     minChars : 4,
16997     /**
16998      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16999      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17000      */
17001     typeAhead: false,
17002     /**
17003      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17004      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17005      */
17006     queryDelay: 500,
17007     /**
17008      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17009      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17010      */
17011     pageSize: 0,
17012     /**
17013      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17014      * when editable = true (defaults to false)
17015      */
17016     selectOnFocus:false,
17017     /**
17018      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17019      */
17020     queryParam: 'query',
17021     /**
17022      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17023      * when mode = 'remote' (defaults to 'Loading...')
17024      */
17025     loadingText: 'Loading...',
17026     /**
17027      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17028      */
17029     resizable: false,
17030     /**
17031      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17032      */
17033     handleHeight : 8,
17034     /**
17035      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17036      * traditional select (defaults to true)
17037      */
17038     editable: true,
17039     /**
17040      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17041      */
17042     allQuery: '',
17043     /**
17044      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17045      */
17046     mode: 'remote',
17047     /**
17048      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17049      * listWidth has a higher value)
17050      */
17051     minListWidth : 70,
17052     /**
17053      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17054      * allow the user to set arbitrary text into the field (defaults to false)
17055      */
17056     forceSelection:false,
17057     /**
17058      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17059      * if typeAhead = true (defaults to 250)
17060      */
17061     typeAheadDelay : 250,
17062     /**
17063      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17064      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17065      */
17066     valueNotFoundText : undefined,
17067     /**
17068      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17069      */
17070     blockFocus : false,
17071     
17072     /**
17073      * @cfg {Boolean} disableClear Disable showing of clear button.
17074      */
17075     disableClear : false,
17076     /**
17077      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17078      */
17079     alwaysQuery : false,
17080     
17081     /**
17082      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17083      */
17084     multiple : false,
17085     
17086     /**
17087      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17088      */
17089     invalidClass : "has-warning",
17090     
17091     /**
17092      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17093      */
17094     validClass : "has-success",
17095     
17096     /**
17097      * @cfg {Boolean} specialFilter (true|false) special filter default false
17098      */
17099     specialFilter : false,
17100     
17101     /**
17102      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17103      */
17104     mobileTouchView : true,
17105     
17106     /**
17107      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17108      */
17109     useNativeIOS : false,
17110     
17111     /**
17112      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17113      */
17114     mobile_restrict_height : false,
17115     
17116     ios_options : false,
17117     
17118     //private
17119     addicon : false,
17120     editicon: false,
17121     
17122     page: 0,
17123     hasQuery: false,
17124     append: false,
17125     loadNext: false,
17126     autoFocus : true,
17127     tickable : false,
17128     btnPosition : 'right',
17129     triggerList : true,
17130     showToggleBtn : true,
17131     animate : true,
17132     emptyResultText: 'Empty',
17133     triggerText : 'Select',
17134     emptyTitle : '',
17135     width : false,
17136     
17137     // element that contains real text value.. (when hidden is used..)
17138     
17139     getAutoCreate : function()
17140     {   
17141         var cfg = false;
17142         //render
17143         /*
17144          * Render classic select for iso
17145          */
17146         
17147         if(Roo.isIOS && this.useNativeIOS){
17148             cfg = this.getAutoCreateNativeIOS();
17149             return cfg;
17150         }
17151         
17152         /*
17153          * Touch Devices
17154          */
17155         
17156         if(Roo.isTouch && this.mobileTouchView){
17157             cfg = this.getAutoCreateTouchView();
17158             return cfg;;
17159         }
17160         
17161         /*
17162          *  Normal ComboBox
17163          */
17164         if(!this.tickable){
17165             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17166             return cfg;
17167         }
17168         
17169         /*
17170          *  ComboBox with tickable selections
17171          */
17172              
17173         var align = this.labelAlign || this.parentLabelAlign();
17174         
17175         cfg = {
17176             cls : 'form-group roo-combobox-tickable' //input-group
17177         };
17178         
17179         var btn_text_select = '';
17180         var btn_text_done = '';
17181         var btn_text_cancel = '';
17182         
17183         if (this.btn_text_show) {
17184             btn_text_select = 'Select';
17185             btn_text_done = 'Done';
17186             btn_text_cancel = 'Cancel'; 
17187         }
17188         
17189         var buttons = {
17190             tag : 'div',
17191             cls : 'tickable-buttons',
17192             cn : [
17193                 {
17194                     tag : 'button',
17195                     type : 'button',
17196                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17197                     //html : this.triggerText
17198                     html: btn_text_select
17199                 },
17200                 {
17201                     tag : 'button',
17202                     type : 'button',
17203                     name : 'ok',
17204                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17205                     //html : 'Done'
17206                     html: btn_text_done
17207                 },
17208                 {
17209                     tag : 'button',
17210                     type : 'button',
17211                     name : 'cancel',
17212                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17213                     //html : 'Cancel'
17214                     html: btn_text_cancel
17215                 }
17216             ]
17217         };
17218         
17219         if(this.editable){
17220             buttons.cn.unshift({
17221                 tag: 'input',
17222                 cls: 'roo-select2-search-field-input'
17223             });
17224         }
17225         
17226         var _this = this;
17227         
17228         Roo.each(buttons.cn, function(c){
17229             if (_this.size) {
17230                 c.cls += ' btn-' + _this.size;
17231             }
17232
17233             if (_this.disabled) {
17234                 c.disabled = true;
17235             }
17236         });
17237         
17238         var box = {
17239             tag: 'div',
17240             style : 'display: contents',
17241             cn: [
17242                 {
17243                     tag: 'input',
17244                     type : 'hidden',
17245                     cls: 'form-hidden-field'
17246                 },
17247                 {
17248                     tag: 'ul',
17249                     cls: 'roo-select2-choices',
17250                     cn:[
17251                         {
17252                             tag: 'li',
17253                             cls: 'roo-select2-search-field',
17254                             cn: [
17255                                 buttons
17256                             ]
17257                         }
17258                     ]
17259                 }
17260             ]
17261         };
17262         
17263         var combobox = {
17264             cls: 'roo-select2-container input-group roo-select2-container-multi',
17265             cn: [
17266                 
17267                 box
17268 //                {
17269 //                    tag: 'ul',
17270 //                    cls: 'typeahead typeahead-long dropdown-menu',
17271 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17272 //                }
17273             ]
17274         };
17275         
17276         if(this.hasFeedback && !this.allowBlank){
17277             
17278             var feedback = {
17279                 tag: 'span',
17280                 cls: 'glyphicon form-control-feedback'
17281             };
17282
17283             combobox.cn.push(feedback);
17284         }
17285         
17286         
17287         
17288         var indicator = {
17289             tag : 'i',
17290             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17291             tooltip : 'This field is required'
17292         };
17293         if (Roo.bootstrap.version == 4) {
17294             indicator = {
17295                 tag : 'i',
17296                 style : 'display:none'
17297             };
17298         }
17299         if (align ==='left' && this.fieldLabel.length) {
17300             
17301             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17302             
17303             cfg.cn = [
17304                 indicator,
17305                 {
17306                     tag: 'label',
17307                     'for' :  id,
17308                     cls : 'control-label col-form-label',
17309                     html : this.fieldLabel
17310
17311                 },
17312                 {
17313                     cls : "", 
17314                     cn: [
17315                         combobox
17316                     ]
17317                 }
17318
17319             ];
17320             
17321             var labelCfg = cfg.cn[1];
17322             var contentCfg = cfg.cn[2];
17323             
17324
17325             if(this.indicatorpos == 'right'){
17326                 
17327                 cfg.cn = [
17328                     {
17329                         tag: 'label',
17330                         'for' :  id,
17331                         cls : 'control-label col-form-label',
17332                         cn : [
17333                             {
17334                                 tag : 'span',
17335                                 html : this.fieldLabel
17336                             },
17337                             indicator
17338                         ]
17339                     },
17340                     {
17341                         cls : "",
17342                         cn: [
17343                             combobox
17344                         ]
17345                     }
17346
17347                 ];
17348                 
17349                 
17350                 
17351                 labelCfg = cfg.cn[0];
17352                 contentCfg = cfg.cn[1];
17353             
17354             }
17355             
17356             if(this.labelWidth > 12){
17357                 labelCfg.style = "width: " + this.labelWidth + 'px';
17358             }
17359             if(this.width * 1 > 0){
17360                 contentCfg.style = "width: " + this.width + 'px';
17361             }
17362             if(this.labelWidth < 13 && this.labelmd == 0){
17363                 this.labelmd = this.labelWidth;
17364             }
17365             
17366             if(this.labellg > 0){
17367                 labelCfg.cls += ' col-lg-' + this.labellg;
17368                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17369             }
17370             
17371             if(this.labelmd > 0){
17372                 labelCfg.cls += ' col-md-' + this.labelmd;
17373                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17374             }
17375             
17376             if(this.labelsm > 0){
17377                 labelCfg.cls += ' col-sm-' + this.labelsm;
17378                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17379             }
17380             
17381             if(this.labelxs > 0){
17382                 labelCfg.cls += ' col-xs-' + this.labelxs;
17383                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17384             }
17385                 
17386                 
17387         } else if ( this.fieldLabel.length) {
17388 //                Roo.log(" label");
17389                  cfg.cn = [
17390                    indicator,
17391                     {
17392                         tag: 'label',
17393                         //cls : 'input-group-addon',
17394                         html : this.fieldLabel
17395                     },
17396                     combobox
17397                 ];
17398                 
17399                 if(this.indicatorpos == 'right'){
17400                     cfg.cn = [
17401                         {
17402                             tag: 'label',
17403                             //cls : 'input-group-addon',
17404                             html : this.fieldLabel
17405                         },
17406                         indicator,
17407                         combobox
17408                     ];
17409                     
17410                 }
17411
17412         } else {
17413             
17414 //                Roo.log(" no label && no align");
17415                 cfg = combobox
17416                      
17417                 
17418         }
17419          
17420         var settings=this;
17421         ['xs','sm','md','lg'].map(function(size){
17422             if (settings[size]) {
17423                 cfg.cls += ' col-' + size + '-' + settings[size];
17424             }
17425         });
17426         
17427         return cfg;
17428         
17429     },
17430     
17431     _initEventsCalled : false,
17432     
17433     // private
17434     initEvents: function()
17435     {   
17436         if (this._initEventsCalled) { // as we call render... prevent looping...
17437             return;
17438         }
17439         this._initEventsCalled = true;
17440         
17441         if (!this.store) {
17442             throw "can not find store for combo";
17443         }
17444         
17445         this.indicator = this.indicatorEl();
17446         
17447         this.store = Roo.factory(this.store, Roo.data);
17448         this.store.parent = this;
17449         
17450         // if we are building from html. then this element is so complex, that we can not really
17451         // use the rendered HTML.
17452         // so we have to trash and replace the previous code.
17453         if (Roo.XComponent.build_from_html) {
17454             // remove this element....
17455             var e = this.el.dom, k=0;
17456             while (e ) { e = e.previousSibling;  ++k;}
17457
17458             this.el.remove();
17459             
17460             this.el=false;
17461             this.rendered = false;
17462             
17463             this.render(this.parent().getChildContainer(true), k);
17464         }
17465         
17466         if(Roo.isIOS && this.useNativeIOS){
17467             this.initIOSView();
17468             return;
17469         }
17470         
17471         /*
17472          * Touch Devices
17473          */
17474         
17475         if(Roo.isTouch && this.mobileTouchView){
17476             this.initTouchView();
17477             return;
17478         }
17479         
17480         if(this.tickable){
17481             this.initTickableEvents();
17482             return;
17483         }
17484         
17485         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17486         
17487         if(this.hiddenName){
17488             
17489             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17490             
17491             this.hiddenField.dom.value =
17492                 this.hiddenValue !== undefined ? this.hiddenValue :
17493                 this.value !== undefined ? this.value : '';
17494
17495             // prevent input submission
17496             this.el.dom.removeAttribute('name');
17497             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17498              
17499              
17500         }
17501         //if(Roo.isGecko){
17502         //    this.el.dom.setAttribute('autocomplete', 'off');
17503         //}
17504         
17505         var cls = 'x-combo-list';
17506         
17507         //this.list = new Roo.Layer({
17508         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17509         //});
17510         
17511         var _this = this;
17512         
17513         (function(){
17514             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17515             _this.list.setWidth(lw);
17516         }).defer(100);
17517         
17518         this.list.on('mouseover', this.onViewOver, this);
17519         this.list.on('mousemove', this.onViewMove, this);
17520         this.list.on('scroll', this.onViewScroll, this);
17521         
17522         /*
17523         this.list.swallowEvent('mousewheel');
17524         this.assetHeight = 0;
17525
17526         if(this.title){
17527             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17528             this.assetHeight += this.header.getHeight();
17529         }
17530
17531         this.innerList = this.list.createChild({cls:cls+'-inner'});
17532         this.innerList.on('mouseover', this.onViewOver, this);
17533         this.innerList.on('mousemove', this.onViewMove, this);
17534         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17535         
17536         if(this.allowBlank && !this.pageSize && !this.disableClear){
17537             this.footer = this.list.createChild({cls:cls+'-ft'});
17538             this.pageTb = new Roo.Toolbar(this.footer);
17539            
17540         }
17541         if(this.pageSize){
17542             this.footer = this.list.createChild({cls:cls+'-ft'});
17543             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17544                     {pageSize: this.pageSize});
17545             
17546         }
17547         
17548         if (this.pageTb && this.allowBlank && !this.disableClear) {
17549             var _this = this;
17550             this.pageTb.add(new Roo.Toolbar.Fill(), {
17551                 cls: 'x-btn-icon x-btn-clear',
17552                 text: '&#160;',
17553                 handler: function()
17554                 {
17555                     _this.collapse();
17556                     _this.clearValue();
17557                     _this.onSelect(false, -1);
17558                 }
17559             });
17560         }
17561         if (this.footer) {
17562             this.assetHeight += this.footer.getHeight();
17563         }
17564         */
17565             
17566         if(!this.tpl){
17567             this.tpl = Roo.bootstrap.version == 4 ?
17568                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17569                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17570         }
17571
17572         this.view = new Roo.View(this.list, this.tpl, {
17573             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17574         });
17575         //this.view.wrapEl.setDisplayed(false);
17576         this.view.on('click', this.onViewClick, this);
17577         
17578         
17579         this.store.on('beforeload', this.onBeforeLoad, this);
17580         this.store.on('load', this.onLoad, this);
17581         this.store.on('loadexception', this.onLoadException, this);
17582         /*
17583         if(this.resizable){
17584             this.resizer = new Roo.Resizable(this.list,  {
17585                pinned:true, handles:'se'
17586             });
17587             this.resizer.on('resize', function(r, w, h){
17588                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17589                 this.listWidth = w;
17590                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17591                 this.restrictHeight();
17592             }, this);
17593             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17594         }
17595         */
17596         if(!this.editable){
17597             this.editable = true;
17598             this.setEditable(false);
17599         }
17600         
17601         /*
17602         
17603         if (typeof(this.events.add.listeners) != 'undefined') {
17604             
17605             this.addicon = this.wrap.createChild(
17606                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17607        
17608             this.addicon.on('click', function(e) {
17609                 this.fireEvent('add', this);
17610             }, this);
17611         }
17612         if (typeof(this.events.edit.listeners) != 'undefined') {
17613             
17614             this.editicon = this.wrap.createChild(
17615                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17616             if (this.addicon) {
17617                 this.editicon.setStyle('margin-left', '40px');
17618             }
17619             this.editicon.on('click', function(e) {
17620                 
17621                 // we fire even  if inothing is selected..
17622                 this.fireEvent('edit', this, this.lastData );
17623                 
17624             }, this);
17625         }
17626         */
17627         
17628         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17629             "up" : function(e){
17630                 this.inKeyMode = true;
17631                 this.selectPrev();
17632             },
17633
17634             "down" : function(e){
17635                 if(!this.isExpanded()){
17636                     this.onTriggerClick();
17637                 }else{
17638                     this.inKeyMode = true;
17639                     this.selectNext();
17640                 }
17641             },
17642
17643             "enter" : function(e){
17644 //                this.onViewClick();
17645                 //return true;
17646                 this.collapse();
17647                 
17648                 if(this.fireEvent("specialkey", this, e)){
17649                     this.onViewClick(false);
17650                 }
17651                 
17652                 return true;
17653             },
17654
17655             "esc" : function(e){
17656                 this.collapse();
17657             },
17658
17659             "tab" : function(e){
17660                 this.collapse();
17661                 
17662                 if(this.fireEvent("specialkey", this, e)){
17663                     this.onViewClick(false);
17664                 }
17665                 
17666                 return true;
17667             },
17668
17669             scope : this,
17670
17671             doRelay : function(foo, bar, hname){
17672                 if(hname == 'down' || this.scope.isExpanded()){
17673                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17674                 }
17675                 return true;
17676             },
17677
17678             forceKeyDown: true
17679         });
17680         
17681         
17682         this.queryDelay = Math.max(this.queryDelay || 10,
17683                 this.mode == 'local' ? 10 : 250);
17684         
17685         
17686         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17687         
17688         if(this.typeAhead){
17689             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17690         }
17691         if(this.editable !== false){
17692             this.inputEl().on("keyup", this.onKeyUp, this);
17693         }
17694         if(this.forceSelection){
17695             this.inputEl().on('blur', this.doForce, this);
17696         }
17697         
17698         if(this.multiple){
17699             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17700             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17701         }
17702     },
17703     
17704     initTickableEvents: function()
17705     {   
17706         this.createList();
17707         
17708         if(this.hiddenName){
17709             
17710             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17711             
17712             this.hiddenField.dom.value =
17713                 this.hiddenValue !== undefined ? this.hiddenValue :
17714                 this.value !== undefined ? this.value : '';
17715
17716             // prevent input submission
17717             this.el.dom.removeAttribute('name');
17718             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17719              
17720              
17721         }
17722         
17723 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17724         
17725         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17726         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17727         if(this.triggerList){
17728             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17729         }
17730          
17731         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17732         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17733         
17734         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17735         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17736         
17737         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17738         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17739         
17740         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17741         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17742         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17743         
17744         this.okBtn.hide();
17745         this.cancelBtn.hide();
17746         
17747         var _this = this;
17748         
17749         (function(){
17750             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17751             _this.list.setWidth(lw);
17752         }).defer(100);
17753         
17754         this.list.on('mouseover', this.onViewOver, this);
17755         this.list.on('mousemove', this.onViewMove, this);
17756         
17757         this.list.on('scroll', this.onViewScroll, this);
17758         
17759         if(!this.tpl){
17760             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17761                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17762         }
17763
17764         this.view = new Roo.View(this.list, this.tpl, {
17765             singleSelect:true,
17766             tickable:true,
17767             parent:this,
17768             store: this.store,
17769             selectedClass: this.selectedClass
17770         });
17771         
17772         //this.view.wrapEl.setDisplayed(false);
17773         this.view.on('click', this.onViewClick, this);
17774         
17775         
17776         
17777         this.store.on('beforeload', this.onBeforeLoad, this);
17778         this.store.on('load', this.onLoad, this);
17779         this.store.on('loadexception', this.onLoadException, this);
17780         
17781         if(this.editable){
17782             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17783                 "up" : function(e){
17784                     this.inKeyMode = true;
17785                     this.selectPrev();
17786                 },
17787
17788                 "down" : function(e){
17789                     this.inKeyMode = true;
17790                     this.selectNext();
17791                 },
17792
17793                 "enter" : function(e){
17794                     if(this.fireEvent("specialkey", this, e)){
17795                         this.onViewClick(false);
17796                     }
17797                     
17798                     return true;
17799                 },
17800
17801                 "esc" : function(e){
17802                     this.onTickableFooterButtonClick(e, false, false);
17803                 },
17804
17805                 "tab" : function(e){
17806                     this.fireEvent("specialkey", this, e);
17807                     
17808                     this.onTickableFooterButtonClick(e, false, false);
17809                     
17810                     return true;
17811                 },
17812
17813                 scope : this,
17814
17815                 doRelay : function(e, fn, key){
17816                     if(this.scope.isExpanded()){
17817                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17818                     }
17819                     return true;
17820                 },
17821
17822                 forceKeyDown: true
17823             });
17824         }
17825         
17826         this.queryDelay = Math.max(this.queryDelay || 10,
17827                 this.mode == 'local' ? 10 : 250);
17828         
17829         
17830         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17831         
17832         if(this.typeAhead){
17833             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17834         }
17835         
17836         if(this.editable !== false){
17837             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17838         }
17839         
17840         this.indicator = this.indicatorEl();
17841         
17842         if(this.indicator){
17843             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17844             this.indicator.hide();
17845         }
17846         
17847     },
17848
17849     onDestroy : function(){
17850         if(this.view){
17851             this.view.setStore(null);
17852             this.view.el.removeAllListeners();
17853             this.view.el.remove();
17854             this.view.purgeListeners();
17855         }
17856         if(this.list){
17857             this.list.dom.innerHTML  = '';
17858         }
17859         
17860         if(this.store){
17861             this.store.un('beforeload', this.onBeforeLoad, this);
17862             this.store.un('load', this.onLoad, this);
17863             this.store.un('loadexception', this.onLoadException, this);
17864         }
17865         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17866     },
17867
17868     // private
17869     fireKey : function(e){
17870         if(e.isNavKeyPress() && !this.list.isVisible()){
17871             this.fireEvent("specialkey", this, e);
17872         }
17873     },
17874
17875     // private
17876     onResize: function(w, h)
17877     {
17878         
17879         
17880 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17881 //        
17882 //        if(typeof w != 'number'){
17883 //            // we do not handle it!?!?
17884 //            return;
17885 //        }
17886 //        var tw = this.trigger.getWidth();
17887 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17888 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17889 //        var x = w - tw;
17890 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17891 //            
17892 //        //this.trigger.setStyle('left', x+'px');
17893 //        
17894 //        if(this.list && this.listWidth === undefined){
17895 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17896 //            this.list.setWidth(lw);
17897 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17898 //        }
17899         
17900     
17901         
17902     },
17903
17904     /**
17905      * Allow or prevent the user from directly editing the field text.  If false is passed,
17906      * the user will only be able to select from the items defined in the dropdown list.  This method
17907      * is the runtime equivalent of setting the 'editable' config option at config time.
17908      * @param {Boolean} value True to allow the user to directly edit the field text
17909      */
17910     setEditable : function(value){
17911         if(value == this.editable){
17912             return;
17913         }
17914         this.editable = value;
17915         if(!value){
17916             this.inputEl().dom.setAttribute('readOnly', true);
17917             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17918             this.inputEl().addClass('x-combo-noedit');
17919         }else{
17920             this.inputEl().dom.removeAttribute('readOnly');
17921             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17922             this.inputEl().removeClass('x-combo-noedit');
17923         }
17924     },
17925
17926     // private
17927     
17928     onBeforeLoad : function(combo,opts){
17929         if(!this.hasFocus){
17930             return;
17931         }
17932          if (!opts.add) {
17933             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17934          }
17935         this.restrictHeight();
17936         this.selectedIndex = -1;
17937     },
17938
17939     // private
17940     onLoad : function(){
17941         
17942         this.hasQuery = false;
17943         
17944         if(!this.hasFocus){
17945             return;
17946         }
17947         
17948         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17949             this.loading.hide();
17950         }
17951         
17952         if(this.store.getCount() > 0){
17953             
17954             this.expand();
17955             this.restrictHeight();
17956             if(this.lastQuery == this.allQuery){
17957                 if(this.editable && !this.tickable){
17958                     this.inputEl().dom.select();
17959                 }
17960                 
17961                 if(
17962                     !this.selectByValue(this.value, true) &&
17963                     this.autoFocus && 
17964                     (
17965                         !this.store.lastOptions ||
17966                         typeof(this.store.lastOptions.add) == 'undefined' || 
17967                         this.store.lastOptions.add != true
17968                     )
17969                 ){
17970                     this.select(0, true);
17971                 }
17972             }else{
17973                 if(this.autoFocus){
17974                     this.selectNext();
17975                 }
17976                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17977                     this.taTask.delay(this.typeAheadDelay);
17978                 }
17979             }
17980         }else{
17981             this.onEmptyResults();
17982         }
17983         
17984         //this.el.focus();
17985     },
17986     // private
17987     onLoadException : function()
17988     {
17989         this.hasQuery = false;
17990         
17991         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17992             this.loading.hide();
17993         }
17994         
17995         if(this.tickable && this.editable){
17996             return;
17997         }
17998         
17999         this.collapse();
18000         // only causes errors at present
18001         //Roo.log(this.store.reader.jsonData);
18002         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18003             // fixme
18004             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18005         //}
18006         
18007         
18008     },
18009     // private
18010     onTypeAhead : function(){
18011         if(this.store.getCount() > 0){
18012             var r = this.store.getAt(0);
18013             var newValue = r.data[this.displayField];
18014             var len = newValue.length;
18015             var selStart = this.getRawValue().length;
18016             
18017             if(selStart != len){
18018                 this.setRawValue(newValue);
18019                 this.selectText(selStart, newValue.length);
18020             }
18021         }
18022     },
18023
18024     // private
18025     onSelect : function(record, index){
18026         
18027         if(this.fireEvent('beforeselect', this, record, index) !== false){
18028         
18029             this.setFromData(index > -1 ? record.data : false);
18030             
18031             this.collapse();
18032             this.fireEvent('select', this, record, index);
18033         }
18034     },
18035
18036     /**
18037      * Returns the currently selected field value or empty string if no value is set.
18038      * @return {String} value The selected value
18039      */
18040     getValue : function()
18041     {
18042         if(Roo.isIOS && this.useNativeIOS){
18043             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18044         }
18045         
18046         if(this.multiple){
18047             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18048         }
18049         
18050         if(this.valueField){
18051             return typeof this.value != 'undefined' ? this.value : '';
18052         }else{
18053             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18054         }
18055     },
18056     
18057     getRawValue : function()
18058     {
18059         if(Roo.isIOS && this.useNativeIOS){
18060             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18061         }
18062         
18063         var v = this.inputEl().getValue();
18064         
18065         return v;
18066     },
18067
18068     /**
18069      * Clears any text/value currently set in the field
18070      */
18071     clearValue : function(){
18072         
18073         if(this.hiddenField){
18074             this.hiddenField.dom.value = '';
18075         }
18076         this.value = '';
18077         this.setRawValue('');
18078         this.lastSelectionText = '';
18079         this.lastData = false;
18080         
18081         var close = this.closeTriggerEl();
18082         
18083         if(close){
18084             close.hide();
18085         }
18086         
18087         this.validate();
18088         
18089     },
18090
18091     /**
18092      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18093      * will be displayed in the field.  If the value does not match the data value of an existing item,
18094      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18095      * Otherwise the field will be blank (although the value will still be set).
18096      * @param {String} value The value to match
18097      */
18098     setValue : function(v)
18099     {
18100         if(Roo.isIOS && this.useNativeIOS){
18101             this.setIOSValue(v);
18102             return;
18103         }
18104         
18105         if(this.multiple){
18106             this.syncValue();
18107             return;
18108         }
18109         
18110         var text = v;
18111         if(this.valueField){
18112             var r = this.findRecord(this.valueField, v);
18113             if(r){
18114                 text = r.data[this.displayField];
18115             }else if(this.valueNotFoundText !== undefined){
18116                 text = this.valueNotFoundText;
18117             }
18118         }
18119         this.lastSelectionText = text;
18120         if(this.hiddenField){
18121             this.hiddenField.dom.value = v;
18122         }
18123         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18124         this.value = v;
18125         
18126         var close = this.closeTriggerEl();
18127         
18128         if(close){
18129             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18130         }
18131         
18132         this.validate();
18133     },
18134     /**
18135      * @property {Object} the last set data for the element
18136      */
18137     
18138     lastData : false,
18139     /**
18140      * Sets the value of the field based on a object which is related to the record format for the store.
18141      * @param {Object} value the value to set as. or false on reset?
18142      */
18143     setFromData : function(o){
18144         
18145         if(this.multiple){
18146             this.addItem(o);
18147             return;
18148         }
18149             
18150         var dv = ''; // display value
18151         var vv = ''; // value value..
18152         this.lastData = o;
18153         if (this.displayField) {
18154             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18155         } else {
18156             // this is an error condition!!!
18157             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18158         }
18159         
18160         if(this.valueField){
18161             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18162         }
18163         
18164         var close = this.closeTriggerEl();
18165         
18166         if(close){
18167             if(dv.length || vv * 1 > 0){
18168                 close.show() ;
18169                 this.blockFocus=true;
18170             } else {
18171                 close.hide();
18172             }             
18173         }
18174         
18175         if(this.hiddenField){
18176             this.hiddenField.dom.value = vv;
18177             
18178             this.lastSelectionText = dv;
18179             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18180             this.value = vv;
18181             return;
18182         }
18183         // no hidden field.. - we store the value in 'value', but still display
18184         // display field!!!!
18185         this.lastSelectionText = dv;
18186         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18187         this.value = vv;
18188         
18189         
18190         
18191     },
18192     // private
18193     reset : function(){
18194         // overridden so that last data is reset..
18195         
18196         if(this.multiple){
18197             this.clearItem();
18198             return;
18199         }
18200         
18201         this.setValue(this.originalValue);
18202         //this.clearInvalid();
18203         this.lastData = false;
18204         if (this.view) {
18205             this.view.clearSelections();
18206         }
18207         
18208         this.validate();
18209     },
18210     // private
18211     findRecord : function(prop, value){
18212         var record;
18213         if(this.store.getCount() > 0){
18214             this.store.each(function(r){
18215                 if(r.data[prop] == value){
18216                     record = r;
18217                     return false;
18218                 }
18219                 return true;
18220             });
18221         }
18222         return record;
18223     },
18224     
18225     getName: function()
18226     {
18227         // returns hidden if it's set..
18228         if (!this.rendered) {return ''};
18229         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18230         
18231     },
18232     // private
18233     onViewMove : function(e, t){
18234         this.inKeyMode = false;
18235     },
18236
18237     // private
18238     onViewOver : function(e, t){
18239         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18240             return;
18241         }
18242         var item = this.view.findItemFromChild(t);
18243         
18244         if(item){
18245             var index = this.view.indexOf(item);
18246             this.select(index, false);
18247         }
18248     },
18249
18250     // private
18251     onViewClick : function(view, doFocus, el, e)
18252     {
18253         var index = this.view.getSelectedIndexes()[0];
18254         
18255         var r = this.store.getAt(index);
18256         
18257         if(this.tickable){
18258             
18259             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18260                 return;
18261             }
18262             
18263             var rm = false;
18264             var _this = this;
18265             
18266             Roo.each(this.tickItems, function(v,k){
18267                 
18268                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18269                     Roo.log(v);
18270                     _this.tickItems.splice(k, 1);
18271                     
18272                     if(typeof(e) == 'undefined' && view == false){
18273                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18274                     }
18275                     
18276                     rm = true;
18277                     return;
18278                 }
18279             });
18280             
18281             if(rm){
18282                 return;
18283             }
18284             
18285             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18286                 this.tickItems.push(r.data);
18287             }
18288             
18289             if(typeof(e) == 'undefined' && view == false){
18290                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18291             }
18292                     
18293             return;
18294         }
18295         
18296         if(r){
18297             this.onSelect(r, index);
18298         }
18299         if(doFocus !== false && !this.blockFocus){
18300             this.inputEl().focus();
18301         }
18302     },
18303
18304     // private
18305     restrictHeight : function(){
18306         //this.innerList.dom.style.height = '';
18307         //var inner = this.innerList.dom;
18308         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18309         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18310         //this.list.beginUpdate();
18311         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18312         this.list.alignTo(this.inputEl(), this.listAlign);
18313         this.list.alignTo(this.inputEl(), this.listAlign);
18314         //this.list.endUpdate();
18315     },
18316
18317     // private
18318     onEmptyResults : function(){
18319         
18320         if(this.tickable && this.editable){
18321             this.hasFocus = false;
18322             this.restrictHeight();
18323             return;
18324         }
18325         
18326         this.collapse();
18327     },
18328
18329     /**
18330      * Returns true if the dropdown list is expanded, else false.
18331      */
18332     isExpanded : function(){
18333         return this.list.isVisible();
18334     },
18335
18336     /**
18337      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18338      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18339      * @param {String} value The data value of the item to select
18340      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18341      * selected item if it is not currently in view (defaults to true)
18342      * @return {Boolean} True if the value matched an item in the list, else false
18343      */
18344     selectByValue : function(v, scrollIntoView){
18345         if(v !== undefined && v !== null){
18346             var r = this.findRecord(this.valueField || this.displayField, v);
18347             if(r){
18348                 this.select(this.store.indexOf(r), scrollIntoView);
18349                 return true;
18350             }
18351         }
18352         return false;
18353     },
18354
18355     /**
18356      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18357      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18358      * @param {Number} index The zero-based index of the list item to select
18359      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18360      * selected item if it is not currently in view (defaults to true)
18361      */
18362     select : function(index, scrollIntoView){
18363         this.selectedIndex = index;
18364         this.view.select(index);
18365         if(scrollIntoView !== false){
18366             var el = this.view.getNode(index);
18367             /*
18368              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18369              */
18370             if(el){
18371                 this.list.scrollChildIntoView(el, false);
18372             }
18373         }
18374     },
18375
18376     // private
18377     selectNext : function(){
18378         var ct = this.store.getCount();
18379         if(ct > 0){
18380             if(this.selectedIndex == -1){
18381                 this.select(0);
18382             }else if(this.selectedIndex < ct-1){
18383                 this.select(this.selectedIndex+1);
18384             }
18385         }
18386     },
18387
18388     // private
18389     selectPrev : function(){
18390         var ct = this.store.getCount();
18391         if(ct > 0){
18392             if(this.selectedIndex == -1){
18393                 this.select(0);
18394             }else if(this.selectedIndex != 0){
18395                 this.select(this.selectedIndex-1);
18396             }
18397         }
18398     },
18399
18400     // private
18401     onKeyUp : function(e){
18402         if(this.editable !== false && !e.isSpecialKey()){
18403             this.lastKey = e.getKey();
18404             this.dqTask.delay(this.queryDelay);
18405         }
18406     },
18407
18408     // private
18409     validateBlur : function(){
18410         return !this.list || !this.list.isVisible();   
18411     },
18412
18413     // private
18414     initQuery : function(){
18415         
18416         var v = this.getRawValue();
18417         
18418         if(this.tickable && this.editable){
18419             v = this.tickableInputEl().getValue();
18420         }
18421         
18422         this.doQuery(v);
18423     },
18424
18425     // private
18426     doForce : function(){
18427         if(this.inputEl().dom.value.length > 0){
18428             this.inputEl().dom.value =
18429                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18430              
18431         }
18432     },
18433
18434     /**
18435      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18436      * query allowing the query action to be canceled if needed.
18437      * @param {String} query The SQL query to execute
18438      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18439      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18440      * saved in the current store (defaults to false)
18441      */
18442     doQuery : function(q, forceAll){
18443         
18444         if(q === undefined || q === null){
18445             q = '';
18446         }
18447         var qe = {
18448             query: q,
18449             forceAll: forceAll,
18450             combo: this,
18451             cancel:false
18452         };
18453         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18454             return false;
18455         }
18456         q = qe.query;
18457         
18458         forceAll = qe.forceAll;
18459         if(forceAll === true || (q.length >= this.minChars)){
18460             
18461             this.hasQuery = true;
18462             
18463             if(this.lastQuery != q || this.alwaysQuery){
18464                 this.lastQuery = q;
18465                 if(this.mode == 'local'){
18466                     this.selectedIndex = -1;
18467                     if(forceAll){
18468                         this.store.clearFilter();
18469                     }else{
18470                         
18471                         if(this.specialFilter){
18472                             this.fireEvent('specialfilter', this);
18473                             this.onLoad();
18474                             return;
18475                         }
18476                         
18477                         this.store.filter(this.displayField, q);
18478                     }
18479                     
18480                     this.store.fireEvent("datachanged", this.store);
18481                     
18482                     this.onLoad();
18483                     
18484                     
18485                 }else{
18486                     
18487                     this.store.baseParams[this.queryParam] = q;
18488                     
18489                     var options = {params : this.getParams(q)};
18490                     
18491                     if(this.loadNext){
18492                         options.add = true;
18493                         options.params.start = this.page * this.pageSize;
18494                     }
18495                     
18496                     this.store.load(options);
18497                     
18498                     /*
18499                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18500                      *  we should expand the list on onLoad
18501                      *  so command out it
18502                      */
18503 //                    this.expand();
18504                 }
18505             }else{
18506                 this.selectedIndex = -1;
18507                 this.onLoad();   
18508             }
18509         }
18510         
18511         this.loadNext = false;
18512     },
18513     
18514     // private
18515     getParams : function(q){
18516         var p = {};
18517         //p[this.queryParam] = q;
18518         
18519         if(this.pageSize){
18520             p.start = 0;
18521             p.limit = this.pageSize;
18522         }
18523         return p;
18524     },
18525
18526     /**
18527      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18528      */
18529     collapse : function(){
18530         if(!this.isExpanded()){
18531             return;
18532         }
18533         
18534         this.list.hide();
18535         
18536         this.hasFocus = false;
18537         
18538         if(this.tickable){
18539             this.okBtn.hide();
18540             this.cancelBtn.hide();
18541             this.trigger.show();
18542             
18543             if(this.editable){
18544                 this.tickableInputEl().dom.value = '';
18545                 this.tickableInputEl().blur();
18546             }
18547             
18548         }
18549         
18550         Roo.get(document).un('mousedown', this.collapseIf, this);
18551         Roo.get(document).un('mousewheel', this.collapseIf, this);
18552         if (!this.editable) {
18553             Roo.get(document).un('keydown', this.listKeyPress, this);
18554         }
18555         this.fireEvent('collapse', this);
18556         
18557         this.validate();
18558     },
18559
18560     // private
18561     collapseIf : function(e){
18562         var in_combo  = e.within(this.el);
18563         var in_list =  e.within(this.list);
18564         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18565         
18566         if (in_combo || in_list || is_list) {
18567             //e.stopPropagation();
18568             return;
18569         }
18570         
18571         if(this.tickable){
18572             this.onTickableFooterButtonClick(e, false, false);
18573         }
18574
18575         this.collapse();
18576         
18577     },
18578
18579     /**
18580      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18581      */
18582     expand : function(){
18583        
18584         if(this.isExpanded() || !this.hasFocus){
18585             return;
18586         }
18587         
18588         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18589         this.list.setWidth(lw);
18590         
18591         Roo.log('expand');
18592         
18593         this.list.show();
18594         
18595         this.restrictHeight();
18596         
18597         if(this.tickable){
18598             
18599             this.tickItems = Roo.apply([], this.item);
18600             
18601             this.okBtn.show();
18602             this.cancelBtn.show();
18603             this.trigger.hide();
18604             
18605             if(this.editable){
18606                 this.tickableInputEl().focus();
18607             }
18608             
18609         }
18610         
18611         Roo.get(document).on('mousedown', this.collapseIf, this);
18612         Roo.get(document).on('mousewheel', this.collapseIf, this);
18613         if (!this.editable) {
18614             Roo.get(document).on('keydown', this.listKeyPress, this);
18615         }
18616         
18617         this.fireEvent('expand', this);
18618     },
18619
18620     // private
18621     // Implements the default empty TriggerField.onTriggerClick function
18622     onTriggerClick : function(e)
18623     {
18624         Roo.log('trigger click');
18625         
18626         if(this.disabled || !this.triggerList){
18627             return;
18628         }
18629         
18630         this.page = 0;
18631         this.loadNext = false;
18632         
18633         if(this.isExpanded()){
18634             this.collapse();
18635             if (!this.blockFocus) {
18636                 this.inputEl().focus();
18637             }
18638             
18639         }else {
18640             this.hasFocus = true;
18641             if(this.triggerAction == 'all') {
18642                 this.doQuery(this.allQuery, true);
18643             } else {
18644                 this.doQuery(this.getRawValue());
18645             }
18646             if (!this.blockFocus) {
18647                 this.inputEl().focus();
18648             }
18649         }
18650     },
18651     
18652     onTickableTriggerClick : function(e)
18653     {
18654         if(this.disabled){
18655             return;
18656         }
18657         
18658         this.page = 0;
18659         this.loadNext = false;
18660         this.hasFocus = true;
18661         
18662         if(this.triggerAction == 'all') {
18663             this.doQuery(this.allQuery, true);
18664         } else {
18665             this.doQuery(this.getRawValue());
18666         }
18667     },
18668     
18669     onSearchFieldClick : function(e)
18670     {
18671         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18672             this.onTickableFooterButtonClick(e, false, false);
18673             return;
18674         }
18675         
18676         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18677             return;
18678         }
18679         
18680         this.page = 0;
18681         this.loadNext = false;
18682         this.hasFocus = true;
18683         
18684         if(this.triggerAction == 'all') {
18685             this.doQuery(this.allQuery, true);
18686         } else {
18687             this.doQuery(this.getRawValue());
18688         }
18689     },
18690     
18691     listKeyPress : function(e)
18692     {
18693         //Roo.log('listkeypress');
18694         // scroll to first matching element based on key pres..
18695         if (e.isSpecialKey()) {
18696             return false;
18697         }
18698         var k = String.fromCharCode(e.getKey()).toUpperCase();
18699         //Roo.log(k);
18700         var match  = false;
18701         var csel = this.view.getSelectedNodes();
18702         var cselitem = false;
18703         if (csel.length) {
18704             var ix = this.view.indexOf(csel[0]);
18705             cselitem  = this.store.getAt(ix);
18706             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18707                 cselitem = false;
18708             }
18709             
18710         }
18711         
18712         this.store.each(function(v) { 
18713             if (cselitem) {
18714                 // start at existing selection.
18715                 if (cselitem.id == v.id) {
18716                     cselitem = false;
18717                 }
18718                 return true;
18719             }
18720                 
18721             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18722                 match = this.store.indexOf(v);
18723                 return false;
18724             }
18725             return true;
18726         }, this);
18727         
18728         if (match === false) {
18729             return true; // no more action?
18730         }
18731         // scroll to?
18732         this.view.select(match);
18733         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18734         sn.scrollIntoView(sn.dom.parentNode, false);
18735     },
18736     
18737     onViewScroll : function(e, t){
18738         
18739         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){
18740             return;
18741         }
18742         
18743         this.hasQuery = true;
18744         
18745         this.loading = this.list.select('.loading', true).first();
18746         
18747         if(this.loading === null){
18748             this.list.createChild({
18749                 tag: 'div',
18750                 cls: 'loading roo-select2-more-results roo-select2-active',
18751                 html: 'Loading more results...'
18752             });
18753             
18754             this.loading = this.list.select('.loading', true).first();
18755             
18756             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18757             
18758             this.loading.hide();
18759         }
18760         
18761         this.loading.show();
18762         
18763         var _combo = this;
18764         
18765         this.page++;
18766         this.loadNext = true;
18767         
18768         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18769         
18770         return;
18771     },
18772     
18773     addItem : function(o)
18774     {   
18775         var dv = ''; // display value
18776         
18777         if (this.displayField) {
18778             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18779         } else {
18780             // this is an error condition!!!
18781             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18782         }
18783         
18784         if(!dv.length){
18785             return;
18786         }
18787         
18788         var choice = this.choices.createChild({
18789             tag: 'li',
18790             cls: 'roo-select2-search-choice',
18791             cn: [
18792                 {
18793                     tag: 'div',
18794                     html: dv
18795                 },
18796                 {
18797                     tag: 'a',
18798                     href: '#',
18799                     cls: 'roo-select2-search-choice-close fa fa-times',
18800                     tabindex: '-1'
18801                 }
18802             ]
18803             
18804         }, this.searchField);
18805         
18806         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18807         
18808         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18809         
18810         this.item.push(o);
18811         
18812         this.lastData = o;
18813         
18814         this.syncValue();
18815         
18816         this.inputEl().dom.value = '';
18817         
18818         this.validate();
18819     },
18820     
18821     onRemoveItem : function(e, _self, o)
18822     {
18823         e.preventDefault();
18824         
18825         this.lastItem = Roo.apply([], this.item);
18826         
18827         var index = this.item.indexOf(o.data) * 1;
18828         
18829         if( index < 0){
18830             Roo.log('not this item?!');
18831             return;
18832         }
18833         
18834         this.item.splice(index, 1);
18835         o.item.remove();
18836         
18837         this.syncValue();
18838         
18839         this.fireEvent('remove', this, e);
18840         
18841         this.validate();
18842         
18843     },
18844     
18845     syncValue : function()
18846     {
18847         if(!this.item.length){
18848             this.clearValue();
18849             return;
18850         }
18851             
18852         var value = [];
18853         var _this = this;
18854         Roo.each(this.item, function(i){
18855             if(_this.valueField){
18856                 value.push(i[_this.valueField]);
18857                 return;
18858             }
18859
18860             value.push(i);
18861         });
18862
18863         this.value = value.join(',');
18864
18865         if(this.hiddenField){
18866             this.hiddenField.dom.value = this.value;
18867         }
18868         
18869         this.store.fireEvent("datachanged", this.store);
18870         
18871         this.validate();
18872     },
18873     
18874     clearItem : function()
18875     {
18876         if(!this.multiple){
18877             return;
18878         }
18879         
18880         this.item = [];
18881         
18882         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18883            c.remove();
18884         });
18885         
18886         this.syncValue();
18887         
18888         this.validate();
18889         
18890         if(this.tickable && !Roo.isTouch){
18891             this.view.refresh();
18892         }
18893     },
18894     
18895     inputEl: function ()
18896     {
18897         if(Roo.isIOS && this.useNativeIOS){
18898             return this.el.select('select.roo-ios-select', true).first();
18899         }
18900         
18901         if(Roo.isTouch && this.mobileTouchView){
18902             return this.el.select('input.form-control',true).first();
18903         }
18904         
18905         if(this.tickable){
18906             return this.searchField;
18907         }
18908         
18909         return this.el.select('input.form-control',true).first();
18910     },
18911     
18912     onTickableFooterButtonClick : function(e, btn, el)
18913     {
18914         e.preventDefault();
18915         
18916         this.lastItem = Roo.apply([], this.item);
18917         
18918         if(btn && btn.name == 'cancel'){
18919             this.tickItems = Roo.apply([], this.item);
18920             this.collapse();
18921             return;
18922         }
18923         
18924         this.clearItem();
18925         
18926         var _this = this;
18927         
18928         Roo.each(this.tickItems, function(o){
18929             _this.addItem(o);
18930         });
18931         
18932         this.collapse();
18933         
18934     },
18935     
18936     validate : function()
18937     {
18938         if(this.getVisibilityEl().hasClass('hidden')){
18939             return true;
18940         }
18941         
18942         var v = this.getRawValue();
18943         
18944         if(this.multiple){
18945             v = this.getValue();
18946         }
18947         
18948         if(this.disabled || this.allowBlank || v.length){
18949             this.markValid();
18950             return true;
18951         }
18952         
18953         this.markInvalid();
18954         return false;
18955     },
18956     
18957     tickableInputEl : function()
18958     {
18959         if(!this.tickable || !this.editable){
18960             return this.inputEl();
18961         }
18962         
18963         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18964     },
18965     
18966     
18967     getAutoCreateTouchView : function()
18968     {
18969         var id = Roo.id();
18970         
18971         var cfg = {
18972             cls: 'form-group' //input-group
18973         };
18974         
18975         var input =  {
18976             tag: 'input',
18977             id : id,
18978             type : this.inputType,
18979             cls : 'form-control x-combo-noedit',
18980             autocomplete: 'new-password',
18981             placeholder : this.placeholder || '',
18982             readonly : true
18983         };
18984         
18985         if (this.name) {
18986             input.name = this.name;
18987         }
18988         
18989         if (this.size) {
18990             input.cls += ' input-' + this.size;
18991         }
18992         
18993         if (this.disabled) {
18994             input.disabled = true;
18995         }
18996         
18997         var inputblock = {
18998             cls : 'roo-combobox-wrap',
18999             cn : [
19000                 input
19001             ]
19002         };
19003         
19004         if(this.before){
19005             inputblock.cls += ' input-group';
19006             
19007             inputblock.cn.unshift({
19008                 tag :'span',
19009                 cls : 'input-group-addon input-group-prepend input-group-text',
19010                 html : this.before
19011             });
19012         }
19013         
19014         if(this.removable && !this.multiple){
19015             inputblock.cls += ' roo-removable';
19016             
19017             inputblock.cn.push({
19018                 tag: 'button',
19019                 html : 'x',
19020                 cls : 'roo-combo-removable-btn close'
19021             });
19022         }
19023
19024         if(this.hasFeedback && !this.allowBlank){
19025             
19026             inputblock.cls += ' has-feedback';
19027             
19028             inputblock.cn.push({
19029                 tag: 'span',
19030                 cls: 'glyphicon form-control-feedback'
19031             });
19032             
19033         }
19034         
19035         if (this.after) {
19036             
19037             inputblock.cls += (this.before) ? '' : ' input-group';
19038             
19039             inputblock.cn.push({
19040                 tag :'span',
19041                 cls : 'input-group-addon input-group-append input-group-text',
19042                 html : this.after
19043             });
19044         }
19045
19046         
19047         var ibwrap = inputblock;
19048         
19049         if(this.multiple){
19050             ibwrap = {
19051                 tag: 'ul',
19052                 cls: 'roo-select2-choices',
19053                 cn:[
19054                     {
19055                         tag: 'li',
19056                         cls: 'roo-select2-search-field',
19057                         cn: [
19058
19059                             inputblock
19060                         ]
19061                     }
19062                 ]
19063             };
19064         
19065             
19066         }
19067         
19068         var combobox = {
19069             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19070             cn: [
19071                 {
19072                     tag: 'input',
19073                     type : 'hidden',
19074                     cls: 'form-hidden-field'
19075                 },
19076                 ibwrap
19077             ]
19078         };
19079         
19080         if(!this.multiple && this.showToggleBtn){
19081             
19082             var caret = {
19083                 cls: 'caret'
19084             };
19085             
19086             if (this.caret != false) {
19087                 caret = {
19088                      tag: 'i',
19089                      cls: 'fa fa-' + this.caret
19090                 };
19091                 
19092             }
19093             
19094             combobox.cn.push({
19095                 tag :'span',
19096                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19097                 cn : [
19098                     Roo.bootstrap.version == 3 ? caret : '',
19099                     {
19100                         tag: 'span',
19101                         cls: 'combobox-clear',
19102                         cn  : [
19103                             {
19104                                 tag : 'i',
19105                                 cls: 'icon-remove'
19106                             }
19107                         ]
19108                     }
19109                 ]
19110
19111             })
19112         }
19113         
19114         if(this.multiple){
19115             combobox.cls += ' roo-select2-container-multi';
19116         }
19117         
19118         var required =  this.allowBlank ?  {
19119                     tag : 'i',
19120                     style: 'display: none'
19121                 } : {
19122                    tag : 'i',
19123                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19124                    tooltip : 'This field is required'
19125                 };
19126         
19127         var align = this.labelAlign || this.parentLabelAlign();
19128         
19129         if (align ==='left' && this.fieldLabel.length) {
19130
19131             cfg.cn = [
19132                 required,
19133                 {
19134                     tag: 'label',
19135                     cls : 'control-label col-form-label',
19136                     html : this.fieldLabel
19137
19138                 },
19139                 {
19140                     cls : 'roo-combobox-wrap ', 
19141                     cn: [
19142                         combobox
19143                     ]
19144                 }
19145             ];
19146             
19147             var labelCfg = cfg.cn[1];
19148             var contentCfg = cfg.cn[2];
19149             
19150
19151             if(this.indicatorpos == 'right'){
19152                 cfg.cn = [
19153                     {
19154                         tag: 'label',
19155                         'for' :  id,
19156                         cls : 'control-label col-form-label',
19157                         cn : [
19158                             {
19159                                 tag : 'span',
19160                                 html : this.fieldLabel
19161                             },
19162                             required
19163                         ]
19164                     },
19165                     {
19166                         cls : "roo-combobox-wrap ",
19167                         cn: [
19168                             combobox
19169                         ]
19170                     }
19171
19172                 ];
19173                 
19174                 labelCfg = cfg.cn[0];
19175                 contentCfg = cfg.cn[1];
19176             }
19177             
19178            
19179             
19180             if(this.labelWidth > 12){
19181                 labelCfg.style = "width: " + this.labelWidth + 'px';
19182             }
19183            
19184             if(this.labelWidth < 13 && this.labelmd == 0){
19185                 this.labelmd = this.labelWidth;
19186             }
19187             
19188             if(this.labellg > 0){
19189                 labelCfg.cls += ' col-lg-' + this.labellg;
19190                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19191             }
19192             
19193             if(this.labelmd > 0){
19194                 labelCfg.cls += ' col-md-' + this.labelmd;
19195                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19196             }
19197             
19198             if(this.labelsm > 0){
19199                 labelCfg.cls += ' col-sm-' + this.labelsm;
19200                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19201             }
19202             
19203             if(this.labelxs > 0){
19204                 labelCfg.cls += ' col-xs-' + this.labelxs;
19205                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19206             }
19207                 
19208                 
19209         } else if ( this.fieldLabel.length) {
19210             cfg.cn = [
19211                required,
19212                 {
19213                     tag: 'label',
19214                     cls : 'control-label',
19215                     html : this.fieldLabel
19216
19217                 },
19218                 {
19219                     cls : '', 
19220                     cn: [
19221                         combobox
19222                     ]
19223                 }
19224             ];
19225             
19226             if(this.indicatorpos == 'right'){
19227                 cfg.cn = [
19228                     {
19229                         tag: 'label',
19230                         cls : 'control-label',
19231                         html : this.fieldLabel,
19232                         cn : [
19233                             required
19234                         ]
19235                     },
19236                     {
19237                         cls : '', 
19238                         cn: [
19239                             combobox
19240                         ]
19241                     }
19242                 ];
19243             }
19244         } else {
19245             cfg.cn = combobox;    
19246         }
19247         
19248         
19249         var settings = this;
19250         
19251         ['xs','sm','md','lg'].map(function(size){
19252             if (settings[size]) {
19253                 cfg.cls += ' col-' + size + '-' + settings[size];
19254             }
19255         });
19256         
19257         return cfg;
19258     },
19259     
19260     initTouchView : function()
19261     {
19262         this.renderTouchView();
19263         
19264         this.touchViewEl.on('scroll', function(){
19265             this.el.dom.scrollTop = 0;
19266         }, this);
19267         
19268         this.originalValue = this.getValue();
19269         
19270         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19271         
19272         this.inputEl().on("click", this.showTouchView, this);
19273         if (this.triggerEl) {
19274             this.triggerEl.on("click", this.showTouchView, this);
19275         }
19276         
19277         
19278         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19279         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19280         
19281         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19282         
19283         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19284         this.store.on('load', this.onTouchViewLoad, this);
19285         this.store.on('loadexception', this.onTouchViewLoadException, this);
19286         
19287         if(this.hiddenName){
19288             
19289             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19290             
19291             this.hiddenField.dom.value =
19292                 this.hiddenValue !== undefined ? this.hiddenValue :
19293                 this.value !== undefined ? this.value : '';
19294         
19295             this.el.dom.removeAttribute('name');
19296             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19297         }
19298         
19299         if(this.multiple){
19300             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19301             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19302         }
19303         
19304         if(this.removable && !this.multiple){
19305             var close = this.closeTriggerEl();
19306             if(close){
19307                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19308                 close.on('click', this.removeBtnClick, this, close);
19309             }
19310         }
19311         /*
19312          * fix the bug in Safari iOS8
19313          */
19314         this.inputEl().on("focus", function(e){
19315             document.activeElement.blur();
19316         }, this);
19317         
19318         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19319         
19320         return;
19321         
19322         
19323     },
19324     
19325     renderTouchView : function()
19326     {
19327         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19328         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19329         
19330         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19331         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19332         
19333         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19334         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19335         this.touchViewBodyEl.setStyle('overflow', 'auto');
19336         
19337         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19338         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19339         
19340         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19341         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19342         
19343     },
19344     
19345     showTouchView : function()
19346     {
19347         if(this.disabled){
19348             return;
19349         }
19350         
19351         this.touchViewHeaderEl.hide();
19352
19353         if(this.modalTitle.length){
19354             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19355             this.touchViewHeaderEl.show();
19356         }
19357
19358         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19359         this.touchViewEl.show();
19360
19361         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19362         
19363         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19364         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19365
19366         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19367
19368         if(this.modalTitle.length){
19369             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19370         }
19371         
19372         this.touchViewBodyEl.setHeight(bodyHeight);
19373
19374         if(this.animate){
19375             var _this = this;
19376             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19377         }else{
19378             this.touchViewEl.addClass(['in','show']);
19379         }
19380         
19381         if(this._touchViewMask){
19382             Roo.get(document.body).addClass("x-body-masked");
19383             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19384             this._touchViewMask.setStyle('z-index', 10000);
19385             this._touchViewMask.addClass('show');
19386         }
19387         
19388         this.doTouchViewQuery();
19389         
19390     },
19391     
19392     hideTouchView : function()
19393     {
19394         this.touchViewEl.removeClass(['in','show']);
19395
19396         if(this.animate){
19397             var _this = this;
19398             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19399         }else{
19400             this.touchViewEl.setStyle('display', 'none');
19401         }
19402         
19403         if(this._touchViewMask){
19404             this._touchViewMask.removeClass('show');
19405             Roo.get(document.body).removeClass("x-body-masked");
19406         }
19407     },
19408     
19409     setTouchViewValue : function()
19410     {
19411         if(this.multiple){
19412             this.clearItem();
19413         
19414             var _this = this;
19415
19416             Roo.each(this.tickItems, function(o){
19417                 this.addItem(o);
19418             }, this);
19419         }
19420         
19421         this.hideTouchView();
19422     },
19423     
19424     doTouchViewQuery : function()
19425     {
19426         var qe = {
19427             query: '',
19428             forceAll: true,
19429             combo: this,
19430             cancel:false
19431         };
19432         
19433         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19434             return false;
19435         }
19436         
19437         if(!this.alwaysQuery || this.mode == 'local'){
19438             this.onTouchViewLoad();
19439             return;
19440         }
19441         
19442         this.store.load();
19443     },
19444     
19445     onTouchViewBeforeLoad : function(combo,opts)
19446     {
19447         return;
19448     },
19449
19450     // private
19451     onTouchViewLoad : function()
19452     {
19453         if(this.store.getCount() < 1){
19454             this.onTouchViewEmptyResults();
19455             return;
19456         }
19457         
19458         this.clearTouchView();
19459         
19460         var rawValue = this.getRawValue();
19461         
19462         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19463         
19464         this.tickItems = [];
19465         
19466         this.store.data.each(function(d, rowIndex){
19467             var row = this.touchViewListGroup.createChild(template);
19468             
19469             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19470                 row.addClass(d.data.cls);
19471             }
19472             
19473             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19474                 var cfg = {
19475                     data : d.data,
19476                     html : d.data[this.displayField]
19477                 };
19478                 
19479                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19480                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19481                 }
19482             }
19483             row.removeClass('selected');
19484             if(!this.multiple && this.valueField &&
19485                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19486             {
19487                 // radio buttons..
19488                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19489                 row.addClass('selected');
19490             }
19491             
19492             if(this.multiple && this.valueField &&
19493                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19494             {
19495                 
19496                 // checkboxes...
19497                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19498                 this.tickItems.push(d.data);
19499             }
19500             
19501             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19502             
19503         }, this);
19504         
19505         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19506         
19507         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19508
19509         if(this.modalTitle.length){
19510             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19511         }
19512
19513         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19514         
19515         if(this.mobile_restrict_height && listHeight < bodyHeight){
19516             this.touchViewBodyEl.setHeight(listHeight);
19517         }
19518         
19519         var _this = this;
19520         
19521         if(firstChecked && listHeight > bodyHeight){
19522             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19523         }
19524         
19525     },
19526     
19527     onTouchViewLoadException : function()
19528     {
19529         this.hideTouchView();
19530     },
19531     
19532     onTouchViewEmptyResults : function()
19533     {
19534         this.clearTouchView();
19535         
19536         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19537         
19538         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19539         
19540     },
19541     
19542     clearTouchView : function()
19543     {
19544         this.touchViewListGroup.dom.innerHTML = '';
19545     },
19546     
19547     onTouchViewClick : function(e, el, o)
19548     {
19549         e.preventDefault();
19550         
19551         var row = o.row;
19552         var rowIndex = o.rowIndex;
19553         
19554         var r = this.store.getAt(rowIndex);
19555         
19556         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19557             
19558             if(!this.multiple){
19559                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19560                     c.dom.removeAttribute('checked');
19561                 }, this);
19562
19563                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19564
19565                 this.setFromData(r.data);
19566
19567                 var close = this.closeTriggerEl();
19568
19569                 if(close){
19570                     close.show();
19571                 }
19572
19573                 this.hideTouchView();
19574
19575                 this.fireEvent('select', this, r, rowIndex);
19576
19577                 return;
19578             }
19579
19580             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19581                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19582                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19583                 return;
19584             }
19585
19586             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19587             this.addItem(r.data);
19588             this.tickItems.push(r.data);
19589         }
19590     },
19591     
19592     getAutoCreateNativeIOS : function()
19593     {
19594         var cfg = {
19595             cls: 'form-group' //input-group,
19596         };
19597         
19598         var combobox =  {
19599             tag: 'select',
19600             cls : 'roo-ios-select'
19601         };
19602         
19603         if (this.name) {
19604             combobox.name = this.name;
19605         }
19606         
19607         if (this.disabled) {
19608             combobox.disabled = true;
19609         }
19610         
19611         var settings = this;
19612         
19613         ['xs','sm','md','lg'].map(function(size){
19614             if (settings[size]) {
19615                 cfg.cls += ' col-' + size + '-' + settings[size];
19616             }
19617         });
19618         
19619         cfg.cn = combobox;
19620         
19621         return cfg;
19622         
19623     },
19624     
19625     initIOSView : function()
19626     {
19627         this.store.on('load', this.onIOSViewLoad, this);
19628         
19629         return;
19630     },
19631     
19632     onIOSViewLoad : function()
19633     {
19634         if(this.store.getCount() < 1){
19635             return;
19636         }
19637         
19638         this.clearIOSView();
19639         
19640         if(this.allowBlank) {
19641             
19642             var default_text = '-- SELECT --';
19643             
19644             if(this.placeholder.length){
19645                 default_text = this.placeholder;
19646             }
19647             
19648             if(this.emptyTitle.length){
19649                 default_text += ' - ' + this.emptyTitle + ' -';
19650             }
19651             
19652             var opt = this.inputEl().createChild({
19653                 tag: 'option',
19654                 value : 0,
19655                 html : default_text
19656             });
19657             
19658             var o = {};
19659             o[this.valueField] = 0;
19660             o[this.displayField] = default_text;
19661             
19662             this.ios_options.push({
19663                 data : o,
19664                 el : opt
19665             });
19666             
19667         }
19668         
19669         this.store.data.each(function(d, rowIndex){
19670             
19671             var html = '';
19672             
19673             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19674                 html = d.data[this.displayField];
19675             }
19676             
19677             var value = '';
19678             
19679             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19680                 value = d.data[this.valueField];
19681             }
19682             
19683             var option = {
19684                 tag: 'option',
19685                 value : value,
19686                 html : html
19687             };
19688             
19689             if(this.value == d.data[this.valueField]){
19690                 option['selected'] = true;
19691             }
19692             
19693             var opt = this.inputEl().createChild(option);
19694             
19695             this.ios_options.push({
19696                 data : d.data,
19697                 el : opt
19698             });
19699             
19700         }, this);
19701         
19702         this.inputEl().on('change', function(){
19703            this.fireEvent('select', this);
19704         }, this);
19705         
19706     },
19707     
19708     clearIOSView: function()
19709     {
19710         this.inputEl().dom.innerHTML = '';
19711         
19712         this.ios_options = [];
19713     },
19714     
19715     setIOSValue: function(v)
19716     {
19717         this.value = v;
19718         
19719         if(!this.ios_options){
19720             return;
19721         }
19722         
19723         Roo.each(this.ios_options, function(opts){
19724            
19725            opts.el.dom.removeAttribute('selected');
19726            
19727            if(opts.data[this.valueField] != v){
19728                return;
19729            }
19730            
19731            opts.el.dom.setAttribute('selected', true);
19732            
19733         }, this);
19734     }
19735
19736     /** 
19737     * @cfg {Boolean} grow 
19738     * @hide 
19739     */
19740     /** 
19741     * @cfg {Number} growMin 
19742     * @hide 
19743     */
19744     /** 
19745     * @cfg {Number} growMax 
19746     * @hide 
19747     */
19748     /**
19749      * @hide
19750      * @method autoSize
19751      */
19752 });
19753
19754 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19755     
19756     header : {
19757         tag: 'div',
19758         cls: 'modal-header',
19759         cn: [
19760             {
19761                 tag: 'h4',
19762                 cls: 'modal-title'
19763             }
19764         ]
19765     },
19766     
19767     body : {
19768         tag: 'div',
19769         cls: 'modal-body',
19770         cn: [
19771             {
19772                 tag: 'ul',
19773                 cls: 'list-group'
19774             }
19775         ]
19776     },
19777     
19778     listItemRadio : {
19779         tag: 'li',
19780         cls: 'list-group-item',
19781         cn: [
19782             {
19783                 tag: 'span',
19784                 cls: 'roo-combobox-list-group-item-value'
19785             },
19786             {
19787                 tag: 'div',
19788                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19789                 cn: [
19790                     {
19791                         tag: 'input',
19792                         type: 'radio'
19793                     },
19794                     {
19795                         tag: 'label'
19796                     }
19797                 ]
19798             }
19799         ]
19800     },
19801     
19802     listItemCheckbox : {
19803         tag: 'li',
19804         cls: 'list-group-item',
19805         cn: [
19806             {
19807                 tag: 'span',
19808                 cls: 'roo-combobox-list-group-item-value'
19809             },
19810             {
19811                 tag: 'div',
19812                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19813                 cn: [
19814                     {
19815                         tag: 'input',
19816                         type: 'checkbox'
19817                     },
19818                     {
19819                         tag: 'label'
19820                     }
19821                 ]
19822             }
19823         ]
19824     },
19825     
19826     emptyResult : {
19827         tag: 'div',
19828         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19829     },
19830     
19831     footer : {
19832         tag: 'div',
19833         cls: 'modal-footer',
19834         cn: [
19835             {
19836                 tag: 'div',
19837                 cls: 'row',
19838                 cn: [
19839                     {
19840                         tag: 'div',
19841                         cls: 'col-xs-6 text-left',
19842                         cn: {
19843                             tag: 'button',
19844                             cls: 'btn btn-danger roo-touch-view-cancel',
19845                             html: 'Cancel'
19846                         }
19847                     },
19848                     {
19849                         tag: 'div',
19850                         cls: 'col-xs-6 text-right',
19851                         cn: {
19852                             tag: 'button',
19853                             cls: 'btn btn-success roo-touch-view-ok',
19854                             html: 'OK'
19855                         }
19856                     }
19857                 ]
19858             }
19859         ]
19860         
19861     }
19862 });
19863
19864 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19865     
19866     touchViewTemplate : {
19867         tag: 'div',
19868         cls: 'modal fade roo-combobox-touch-view',
19869         cn: [
19870             {
19871                 tag: 'div',
19872                 cls: 'modal-dialog',
19873                 style : 'position:fixed', // we have to fix position....
19874                 cn: [
19875                     {
19876                         tag: 'div',
19877                         cls: 'modal-content',
19878                         cn: [
19879                             Roo.bootstrap.form.ComboBox.header,
19880                             Roo.bootstrap.form.ComboBox.body,
19881                             Roo.bootstrap.form.ComboBox.footer
19882                         ]
19883                     }
19884                 ]
19885             }
19886         ]
19887     }
19888 });/*
19889  * Based on:
19890  * Ext JS Library 1.1.1
19891  * Copyright(c) 2006-2007, Ext JS, LLC.
19892  *
19893  * Originally Released Under LGPL - original licence link has changed is not relivant.
19894  *
19895  * Fork - LGPL
19896  * <script type="text/javascript">
19897  */
19898
19899 /**
19900  * @class Roo.View
19901  * @extends Roo.util.Observable
19902  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19903  * This class also supports single and multi selection modes. <br>
19904  * Create a data model bound view:
19905  <pre><code>
19906  var store = new Roo.data.Store(...);
19907
19908  var view = new Roo.View({
19909     el : "my-element",
19910     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19911  
19912     singleSelect: true,
19913     selectedClass: "ydataview-selected",
19914     store: store
19915  });
19916
19917  // listen for node click?
19918  view.on("click", function(vw, index, node, e){
19919  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19920  });
19921
19922  // load XML data
19923  dataModel.load("foobar.xml");
19924  </code></pre>
19925  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19926  * <br><br>
19927  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19928  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19929  * 
19930  * Note: old style constructor is still suported (container, template, config)
19931  * 
19932  * @constructor
19933  * Create a new View
19934  * @param {Object} config The config object
19935  * 
19936  */
19937 Roo.View = function(config, depreciated_tpl, depreciated_config){
19938     
19939     this.parent = false;
19940     
19941     if (typeof(depreciated_tpl) == 'undefined') {
19942         // new way.. - universal constructor.
19943         Roo.apply(this, config);
19944         this.el  = Roo.get(this.el);
19945     } else {
19946         // old format..
19947         this.el  = Roo.get(config);
19948         this.tpl = depreciated_tpl;
19949         Roo.apply(this, depreciated_config);
19950     }
19951     this.wrapEl  = this.el.wrap().wrap();
19952     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19953     
19954     
19955     if(typeof(this.tpl) == "string"){
19956         this.tpl = new Roo.Template(this.tpl);
19957     } else {
19958         // support xtype ctors..
19959         this.tpl = new Roo.factory(this.tpl, Roo);
19960     }
19961     
19962     
19963     this.tpl.compile();
19964     
19965     /** @private */
19966     this.addEvents({
19967         /**
19968          * @event beforeclick
19969          * Fires before a click is processed. Returns false to cancel the default action.
19970          * @param {Roo.View} this
19971          * @param {Number} index The index of the target node
19972          * @param {HTMLElement} node The target node
19973          * @param {Roo.EventObject} e The raw event object
19974          */
19975             "beforeclick" : true,
19976         /**
19977          * @event click
19978          * Fires when a template node is clicked.
19979          * @param {Roo.View} this
19980          * @param {Number} index The index of the target node
19981          * @param {HTMLElement} node The target node
19982          * @param {Roo.EventObject} e The raw event object
19983          */
19984             "click" : true,
19985         /**
19986          * @event dblclick
19987          * Fires when a template node is double clicked.
19988          * @param {Roo.View} this
19989          * @param {Number} index The index of the target node
19990          * @param {HTMLElement} node The target node
19991          * @param {Roo.EventObject} e The raw event object
19992          */
19993             "dblclick" : true,
19994         /**
19995          * @event contextmenu
19996          * Fires when a template node is right clicked.
19997          * @param {Roo.View} this
19998          * @param {Number} index The index of the target node
19999          * @param {HTMLElement} node The target node
20000          * @param {Roo.EventObject} e The raw event object
20001          */
20002             "contextmenu" : true,
20003         /**
20004          * @event selectionchange
20005          * Fires when the selected nodes change.
20006          * @param {Roo.View} this
20007          * @param {Array} selections Array of the selected nodes
20008          */
20009             "selectionchange" : true,
20010     
20011         /**
20012          * @event beforeselect
20013          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20014          * @param {Roo.View} this
20015          * @param {HTMLElement} node The node to be selected
20016          * @param {Array} selections Array of currently selected nodes
20017          */
20018             "beforeselect" : true,
20019         /**
20020          * @event preparedata
20021          * Fires on every row to render, to allow you to change the data.
20022          * @param {Roo.View} this
20023          * @param {Object} data to be rendered (change this)
20024          */
20025           "preparedata" : true
20026           
20027           
20028         });
20029
20030
20031
20032     this.el.on({
20033         "click": this.onClick,
20034         "dblclick": this.onDblClick,
20035         "contextmenu": this.onContextMenu,
20036         scope:this
20037     });
20038
20039     this.selections = [];
20040     this.nodes = [];
20041     this.cmp = new Roo.CompositeElementLite([]);
20042     if(this.store){
20043         this.store = Roo.factory(this.store, Roo.data);
20044         this.setStore(this.store, true);
20045     }
20046     
20047     if ( this.footer && this.footer.xtype) {
20048            
20049          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20050         
20051         this.footer.dataSource = this.store;
20052         this.footer.container = fctr;
20053         this.footer = Roo.factory(this.footer, Roo);
20054         fctr.insertFirst(this.el);
20055         
20056         // this is a bit insane - as the paging toolbar seems to detach the el..
20057 //        dom.parentNode.parentNode.parentNode
20058          // they get detached?
20059     }
20060     
20061     
20062     Roo.View.superclass.constructor.call(this);
20063     
20064     
20065 };
20066
20067 Roo.extend(Roo.View, Roo.util.Observable, {
20068     
20069      /**
20070      * @cfg {Roo.data.Store} store Data store to load data from.
20071      */
20072     store : false,
20073     
20074     /**
20075      * @cfg {String|Roo.Element} el The container element.
20076      */
20077     el : '',
20078     
20079     /**
20080      * @cfg {String|Roo.Template} tpl The template used by this View 
20081      */
20082     tpl : false,
20083     /**
20084      * @cfg {String} dataName the named area of the template to use as the data area
20085      *                          Works with domtemplates roo-name="name"
20086      */
20087     dataName: false,
20088     /**
20089      * @cfg {String} selectedClass The css class to add to selected nodes
20090      */
20091     selectedClass : "x-view-selected",
20092      /**
20093      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20094      */
20095     emptyText : "",
20096     
20097     /**
20098      * @cfg {String} text to display on mask (default Loading)
20099      */
20100     mask : false,
20101     /**
20102      * @cfg {Boolean} multiSelect Allow multiple selection
20103      */
20104     multiSelect : false,
20105     /**
20106      * @cfg {Boolean} singleSelect Allow single selection
20107      */
20108     singleSelect:  false,
20109     
20110     /**
20111      * @cfg {Boolean} toggleSelect - selecting 
20112      */
20113     toggleSelect : false,
20114     
20115     /**
20116      * @cfg {Boolean} tickable - selecting 
20117      */
20118     tickable : false,
20119     
20120     /**
20121      * Returns the element this view is bound to.
20122      * @return {Roo.Element}
20123      */
20124     getEl : function(){
20125         return this.wrapEl;
20126     },
20127     
20128     
20129
20130     /**
20131      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20132      */
20133     refresh : function(){
20134         //Roo.log('refresh');
20135         var t = this.tpl;
20136         
20137         // if we are using something like 'domtemplate', then
20138         // the what gets used is:
20139         // t.applySubtemplate(NAME, data, wrapping data..)
20140         // the outer template then get' applied with
20141         //     the store 'extra data'
20142         // and the body get's added to the
20143         //      roo-name="data" node?
20144         //      <span class='roo-tpl-{name}'></span> ?????
20145         
20146         
20147         
20148         this.clearSelections();
20149         this.el.update("");
20150         var html = [];
20151         var records = this.store.getRange();
20152         if(records.length < 1) {
20153             
20154             // is this valid??  = should it render a template??
20155             
20156             this.el.update(this.emptyText);
20157             return;
20158         }
20159         var el = this.el;
20160         if (this.dataName) {
20161             this.el.update(t.apply(this.store.meta)); //????
20162             el = this.el.child('.roo-tpl-' + this.dataName);
20163         }
20164         
20165         for(var i = 0, len = records.length; i < len; i++){
20166             var data = this.prepareData(records[i].data, i, records[i]);
20167             this.fireEvent("preparedata", this, data, i, records[i]);
20168             
20169             var d = Roo.apply({}, data);
20170             
20171             if(this.tickable){
20172                 Roo.apply(d, {'roo-id' : Roo.id()});
20173                 
20174                 var _this = this;
20175             
20176                 Roo.each(this.parent.item, function(item){
20177                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20178                         return;
20179                     }
20180                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20181                 });
20182             }
20183             
20184             html[html.length] = Roo.util.Format.trim(
20185                 this.dataName ?
20186                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20187                     t.apply(d)
20188             );
20189         }
20190         
20191         
20192         
20193         el.update(html.join(""));
20194         this.nodes = el.dom.childNodes;
20195         this.updateIndexes(0);
20196     },
20197     
20198
20199     /**
20200      * Function to override to reformat the data that is sent to
20201      * the template for each node.
20202      * DEPRICATED - use the preparedata event handler.
20203      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20204      * a JSON object for an UpdateManager bound view).
20205      */
20206     prepareData : function(data, index, record)
20207     {
20208         this.fireEvent("preparedata", this, data, index, record);
20209         return data;
20210     },
20211
20212     onUpdate : function(ds, record){
20213         // Roo.log('on update');   
20214         this.clearSelections();
20215         var index = this.store.indexOf(record);
20216         var n = this.nodes[index];
20217         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20218         n.parentNode.removeChild(n);
20219         this.updateIndexes(index, index);
20220     },
20221
20222     
20223     
20224 // --------- FIXME     
20225     onAdd : function(ds, records, index)
20226     {
20227         //Roo.log(['on Add', ds, records, index] );        
20228         this.clearSelections();
20229         if(this.nodes.length == 0){
20230             this.refresh();
20231             return;
20232         }
20233         var n = this.nodes[index];
20234         for(var i = 0, len = records.length; i < len; i++){
20235             var d = this.prepareData(records[i].data, i, records[i]);
20236             if(n){
20237                 this.tpl.insertBefore(n, d);
20238             }else{
20239                 
20240                 this.tpl.append(this.el, d);
20241             }
20242         }
20243         this.updateIndexes(index);
20244     },
20245
20246     onRemove : function(ds, record, index){
20247        // Roo.log('onRemove');
20248         this.clearSelections();
20249         var el = this.dataName  ?
20250             this.el.child('.roo-tpl-' + this.dataName) :
20251             this.el; 
20252         
20253         el.dom.removeChild(this.nodes[index]);
20254         this.updateIndexes(index);
20255     },
20256
20257     /**
20258      * Refresh an individual node.
20259      * @param {Number} index
20260      */
20261     refreshNode : function(index){
20262         this.onUpdate(this.store, this.store.getAt(index));
20263     },
20264
20265     updateIndexes : function(startIndex, endIndex){
20266         var ns = this.nodes;
20267         startIndex = startIndex || 0;
20268         endIndex = endIndex || ns.length - 1;
20269         for(var i = startIndex; i <= endIndex; i++){
20270             ns[i].nodeIndex = i;
20271         }
20272     },
20273
20274     /**
20275      * Changes the data store this view uses and refresh the view.
20276      * @param {Store} store
20277      */
20278     setStore : function(store, initial){
20279         if(!initial && this.store){
20280             this.store.un("datachanged", this.refresh);
20281             this.store.un("add", this.onAdd);
20282             this.store.un("remove", this.onRemove);
20283             this.store.un("update", this.onUpdate);
20284             this.store.un("clear", this.refresh);
20285             this.store.un("beforeload", this.onBeforeLoad);
20286             this.store.un("load", this.onLoad);
20287             this.store.un("loadexception", this.onLoad);
20288         }
20289         if(store){
20290           
20291             store.on("datachanged", this.refresh, this);
20292             store.on("add", this.onAdd, this);
20293             store.on("remove", this.onRemove, this);
20294             store.on("update", this.onUpdate, this);
20295             store.on("clear", this.refresh, this);
20296             store.on("beforeload", this.onBeforeLoad, this);
20297             store.on("load", this.onLoad, this);
20298             store.on("loadexception", this.onLoad, this);
20299         }
20300         
20301         if(store){
20302             this.refresh();
20303         }
20304     },
20305     /**
20306      * onbeforeLoad - masks the loading area.
20307      *
20308      */
20309     onBeforeLoad : function(store,opts)
20310     {
20311          //Roo.log('onBeforeLoad');   
20312         if (!opts.add) {
20313             this.el.update("");
20314         }
20315         this.el.mask(this.mask ? this.mask : "Loading" ); 
20316     },
20317     onLoad : function ()
20318     {
20319         this.el.unmask();
20320     },
20321     
20322
20323     /**
20324      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20325      * @param {HTMLElement} node
20326      * @return {HTMLElement} The template node
20327      */
20328     findItemFromChild : function(node){
20329         var el = this.dataName  ?
20330             this.el.child('.roo-tpl-' + this.dataName,true) :
20331             this.el.dom; 
20332         
20333         if(!node || node.parentNode == el){
20334                     return node;
20335             }
20336             var p = node.parentNode;
20337             while(p && p != el){
20338             if(p.parentNode == el){
20339                 return p;
20340             }
20341             p = p.parentNode;
20342         }
20343             return null;
20344     },
20345
20346     /** @ignore */
20347     onClick : function(e){
20348         var item = this.findItemFromChild(e.getTarget());
20349         if(item){
20350             var index = this.indexOf(item);
20351             if(this.onItemClick(item, index, e) !== false){
20352                 this.fireEvent("click", this, index, item, e);
20353             }
20354         }else{
20355             this.clearSelections();
20356         }
20357     },
20358
20359     /** @ignore */
20360     onContextMenu : function(e){
20361         var item = this.findItemFromChild(e.getTarget());
20362         if(item){
20363             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20364         }
20365     },
20366
20367     /** @ignore */
20368     onDblClick : function(e){
20369         var item = this.findItemFromChild(e.getTarget());
20370         if(item){
20371             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20372         }
20373     },
20374
20375     onItemClick : function(item, index, e)
20376     {
20377         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20378             return false;
20379         }
20380         if (this.toggleSelect) {
20381             var m = this.isSelected(item) ? 'unselect' : 'select';
20382             //Roo.log(m);
20383             var _t = this;
20384             _t[m](item, true, false);
20385             return true;
20386         }
20387         if(this.multiSelect || this.singleSelect){
20388             if(this.multiSelect && e.shiftKey && this.lastSelection){
20389                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20390             }else{
20391                 this.select(item, this.multiSelect && e.ctrlKey);
20392                 this.lastSelection = item;
20393             }
20394             
20395             if(!this.tickable){
20396                 e.preventDefault();
20397             }
20398             
20399         }
20400         return true;
20401     },
20402
20403     /**
20404      * Get the number of selected nodes.
20405      * @return {Number}
20406      */
20407     getSelectionCount : function(){
20408         return this.selections.length;
20409     },
20410
20411     /**
20412      * Get the currently selected nodes.
20413      * @return {Array} An array of HTMLElements
20414      */
20415     getSelectedNodes : function(){
20416         return this.selections;
20417     },
20418
20419     /**
20420      * Get the indexes of the selected nodes.
20421      * @return {Array}
20422      */
20423     getSelectedIndexes : function(){
20424         var indexes = [], s = this.selections;
20425         for(var i = 0, len = s.length; i < len; i++){
20426             indexes.push(s[i].nodeIndex);
20427         }
20428         return indexes;
20429     },
20430
20431     /**
20432      * Clear all selections
20433      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20434      */
20435     clearSelections : function(suppressEvent){
20436         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20437             this.cmp.elements = this.selections;
20438             this.cmp.removeClass(this.selectedClass);
20439             this.selections = [];
20440             if(!suppressEvent){
20441                 this.fireEvent("selectionchange", this, this.selections);
20442             }
20443         }
20444     },
20445
20446     /**
20447      * Returns true if the passed node is selected
20448      * @param {HTMLElement/Number} node The node or node index
20449      * @return {Boolean}
20450      */
20451     isSelected : function(node){
20452         var s = this.selections;
20453         if(s.length < 1){
20454             return false;
20455         }
20456         node = this.getNode(node);
20457         return s.indexOf(node) !== -1;
20458     },
20459
20460     /**
20461      * Selects nodes.
20462      * @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
20463      * @param {Boolean} keepExisting (optional) true to keep existing selections
20464      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20465      */
20466     select : function(nodeInfo, keepExisting, suppressEvent){
20467         if(nodeInfo instanceof Array){
20468             if(!keepExisting){
20469                 this.clearSelections(true);
20470             }
20471             for(var i = 0, len = nodeInfo.length; i < len; i++){
20472                 this.select(nodeInfo[i], true, true);
20473             }
20474             return;
20475         } 
20476         var node = this.getNode(nodeInfo);
20477         if(!node || this.isSelected(node)){
20478             return; // already selected.
20479         }
20480         if(!keepExisting){
20481             this.clearSelections(true);
20482         }
20483         
20484         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20485             Roo.fly(node).addClass(this.selectedClass);
20486             this.selections.push(node);
20487             if(!suppressEvent){
20488                 this.fireEvent("selectionchange", this, this.selections);
20489             }
20490         }
20491         
20492         
20493     },
20494       /**
20495      * Unselects nodes.
20496      * @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
20497      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20498      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20499      */
20500     unselect : function(nodeInfo, keepExisting, suppressEvent)
20501     {
20502         if(nodeInfo instanceof Array){
20503             Roo.each(this.selections, function(s) {
20504                 this.unselect(s, nodeInfo);
20505             }, this);
20506             return;
20507         }
20508         var node = this.getNode(nodeInfo);
20509         if(!node || !this.isSelected(node)){
20510             //Roo.log("not selected");
20511             return; // not selected.
20512         }
20513         // fireevent???
20514         var ns = [];
20515         Roo.each(this.selections, function(s) {
20516             if (s == node ) {
20517                 Roo.fly(node).removeClass(this.selectedClass);
20518
20519                 return;
20520             }
20521             ns.push(s);
20522         },this);
20523         
20524         this.selections= ns;
20525         this.fireEvent("selectionchange", this, this.selections);
20526     },
20527
20528     /**
20529      * Gets a template node.
20530      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20531      * @return {HTMLElement} The node or null if it wasn't found
20532      */
20533     getNode : function(nodeInfo){
20534         if(typeof nodeInfo == "string"){
20535             return document.getElementById(nodeInfo);
20536         }else if(typeof nodeInfo == "number"){
20537             return this.nodes[nodeInfo];
20538         }
20539         return nodeInfo;
20540     },
20541
20542     /**
20543      * Gets a range template nodes.
20544      * @param {Number} startIndex
20545      * @param {Number} endIndex
20546      * @return {Array} An array of nodes
20547      */
20548     getNodes : function(start, end){
20549         var ns = this.nodes;
20550         start = start || 0;
20551         end = typeof end == "undefined" ? ns.length - 1 : end;
20552         var nodes = [];
20553         if(start <= end){
20554             for(var i = start; i <= end; i++){
20555                 nodes.push(ns[i]);
20556             }
20557         } else{
20558             for(var i = start; i >= end; i--){
20559                 nodes.push(ns[i]);
20560             }
20561         }
20562         return nodes;
20563     },
20564
20565     /**
20566      * Finds the index of the passed node
20567      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20568      * @return {Number} The index of the node or -1
20569      */
20570     indexOf : function(node){
20571         node = this.getNode(node);
20572         if(typeof node.nodeIndex == "number"){
20573             return node.nodeIndex;
20574         }
20575         var ns = this.nodes;
20576         for(var i = 0, len = ns.length; i < len; i++){
20577             if(ns[i] == node){
20578                 return i;
20579             }
20580         }
20581         return -1;
20582     }
20583 });
20584 /*
20585  * - LGPL
20586  *
20587  * based on jquery fullcalendar
20588  * 
20589  */
20590
20591 Roo.bootstrap = Roo.bootstrap || {};
20592 /**
20593  * @class Roo.bootstrap.Calendar
20594  * @extends Roo.bootstrap.Component
20595  * Bootstrap Calendar class
20596  * @cfg {Boolean} loadMask (true|false) default false
20597  * @cfg {Object} header generate the user specific header of the calendar, default false
20598
20599  * @constructor
20600  * Create a new Container
20601  * @param {Object} config The config object
20602  */
20603
20604
20605
20606 Roo.bootstrap.Calendar = function(config){
20607     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20608      this.addEvents({
20609         /**
20610              * @event select
20611              * Fires when a date is selected
20612              * @param {DatePicker} this
20613              * @param {Date} date The selected date
20614              */
20615         'select': true,
20616         /**
20617              * @event monthchange
20618              * Fires when the displayed month changes 
20619              * @param {DatePicker} this
20620              * @param {Date} date The selected month
20621              */
20622         'monthchange': true,
20623         /**
20624              * @event evententer
20625              * Fires when mouse over an event
20626              * @param {Calendar} this
20627              * @param {event} Event
20628              */
20629         'evententer': true,
20630         /**
20631              * @event eventleave
20632              * Fires when the mouse leaves an
20633              * @param {Calendar} this
20634              * @param {event}
20635              */
20636         'eventleave': true,
20637         /**
20638              * @event eventclick
20639              * Fires when the mouse click an
20640              * @param {Calendar} this
20641              * @param {event}
20642              */
20643         'eventclick': true
20644         
20645     });
20646
20647 };
20648
20649 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20650     
20651           /**
20652      * @cfg {Roo.data.Store} store
20653      * The data source for the calendar
20654      */
20655         store : false,
20656      /**
20657      * @cfg {Number} startDay
20658      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20659      */
20660     startDay : 0,
20661     
20662     loadMask : false,
20663     
20664     header : false,
20665       
20666     getAutoCreate : function(){
20667         
20668         
20669         var fc_button = function(name, corner, style, content ) {
20670             return Roo.apply({},{
20671                 tag : 'span',
20672                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20673                          (corner.length ?
20674                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20675                             ''
20676                         ),
20677                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20678                 unselectable: 'on'
20679             });
20680         };
20681         
20682         var header = {};
20683         
20684         if(!this.header){
20685             header = {
20686                 tag : 'table',
20687                 cls : 'fc-header',
20688                 style : 'width:100%',
20689                 cn : [
20690                     {
20691                         tag: 'tr',
20692                         cn : [
20693                             {
20694                                 tag : 'td',
20695                                 cls : 'fc-header-left',
20696                                 cn : [
20697                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20698                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20699                                     { tag: 'span', cls: 'fc-header-space' },
20700                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20701
20702
20703                                 ]
20704                             },
20705
20706                             {
20707                                 tag : 'td',
20708                                 cls : 'fc-header-center',
20709                                 cn : [
20710                                     {
20711                                         tag: 'span',
20712                                         cls: 'fc-header-title',
20713                                         cn : {
20714                                             tag: 'H2',
20715                                             html : 'month / year'
20716                                         }
20717                                     }
20718
20719                                 ]
20720                             },
20721                             {
20722                                 tag : 'td',
20723                                 cls : 'fc-header-right',
20724                                 cn : [
20725                               /*      fc_button('month', 'left', '', 'month' ),
20726                                     fc_button('week', '', '', 'week' ),
20727                                     fc_button('day', 'right', '', 'day' )
20728                                 */    
20729
20730                                 ]
20731                             }
20732
20733                         ]
20734                     }
20735                 ]
20736             };
20737         }
20738         
20739         header = this.header;
20740         
20741        
20742         var cal_heads = function() {
20743             var ret = [];
20744             // fixme - handle this.
20745             
20746             for (var i =0; i < Date.dayNames.length; i++) {
20747                 var d = Date.dayNames[i];
20748                 ret.push({
20749                     tag: 'th',
20750                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20751                     html : d.substring(0,3)
20752                 });
20753                 
20754             }
20755             ret[0].cls += ' fc-first';
20756             ret[6].cls += ' fc-last';
20757             return ret;
20758         };
20759         var cal_cell = function(n) {
20760             return  {
20761                 tag: 'td',
20762                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20763                 cn : [
20764                     {
20765                         cn : [
20766                             {
20767                                 cls: 'fc-day-number',
20768                                 html: 'D'
20769                             },
20770                             {
20771                                 cls: 'fc-day-content',
20772                              
20773                                 cn : [
20774                                      {
20775                                         style: 'position: relative;' // height: 17px;
20776                                     }
20777                                 ]
20778                             }
20779                             
20780                             
20781                         ]
20782                     }
20783                 ]
20784                 
20785             }
20786         };
20787         var cal_rows = function() {
20788             
20789             var ret = [];
20790             for (var r = 0; r < 6; r++) {
20791                 var row= {
20792                     tag : 'tr',
20793                     cls : 'fc-week',
20794                     cn : []
20795                 };
20796                 
20797                 for (var i =0; i < Date.dayNames.length; i++) {
20798                     var d = Date.dayNames[i];
20799                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20800
20801                 }
20802                 row.cn[0].cls+=' fc-first';
20803                 row.cn[0].cn[0].style = 'min-height:90px';
20804                 row.cn[6].cls+=' fc-last';
20805                 ret.push(row);
20806                 
20807             }
20808             ret[0].cls += ' fc-first';
20809             ret[4].cls += ' fc-prev-last';
20810             ret[5].cls += ' fc-last';
20811             return ret;
20812             
20813         };
20814         
20815         var cal_table = {
20816             tag: 'table',
20817             cls: 'fc-border-separate',
20818             style : 'width:100%',
20819             cellspacing  : 0,
20820             cn : [
20821                 { 
20822                     tag: 'thead',
20823                     cn : [
20824                         { 
20825                             tag: 'tr',
20826                             cls : 'fc-first fc-last',
20827                             cn : cal_heads()
20828                         }
20829                     ]
20830                 },
20831                 { 
20832                     tag: 'tbody',
20833                     cn : cal_rows()
20834                 }
20835                   
20836             ]
20837         };
20838          
20839          var cfg = {
20840             cls : 'fc fc-ltr',
20841             cn : [
20842                 header,
20843                 {
20844                     cls : 'fc-content',
20845                     style : "position: relative;",
20846                     cn : [
20847                         {
20848                             cls : 'fc-view fc-view-month fc-grid',
20849                             style : 'position: relative',
20850                             unselectable : 'on',
20851                             cn : [
20852                                 {
20853                                     cls : 'fc-event-container',
20854                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20855                                 },
20856                                 cal_table
20857                             ]
20858                         }
20859                     ]
20860     
20861                 }
20862            ] 
20863             
20864         };
20865         
20866          
20867         
20868         return cfg;
20869     },
20870     
20871     
20872     initEvents : function()
20873     {
20874         if(!this.store){
20875             throw "can not find store for calendar";
20876         }
20877         
20878         var mark = {
20879             tag: "div",
20880             cls:"x-dlg-mask",
20881             style: "text-align:center",
20882             cn: [
20883                 {
20884                     tag: "div",
20885                     style: "background-color:white;width:50%;margin:250 auto",
20886                     cn: [
20887                         {
20888                             tag: "img",
20889                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20890                         },
20891                         {
20892                             tag: "span",
20893                             html: "Loading"
20894                         }
20895                         
20896                     ]
20897                 }
20898             ]
20899         };
20900         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20901         
20902         var size = this.el.select('.fc-content', true).first().getSize();
20903         this.maskEl.setSize(size.width, size.height);
20904         this.maskEl.enableDisplayMode("block");
20905         if(!this.loadMask){
20906             this.maskEl.hide();
20907         }
20908         
20909         this.store = Roo.factory(this.store, Roo.data);
20910         this.store.on('load', this.onLoad, this);
20911         this.store.on('beforeload', this.onBeforeLoad, this);
20912         
20913         this.resize();
20914         
20915         this.cells = this.el.select('.fc-day',true);
20916         //Roo.log(this.cells);
20917         this.textNodes = this.el.query('.fc-day-number');
20918         this.cells.addClassOnOver('fc-state-hover');
20919         
20920         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20921         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20922         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20923         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20924         
20925         this.on('monthchange', this.onMonthChange, this);
20926         
20927         this.update(new Date().clearTime());
20928     },
20929     
20930     resize : function() {
20931         var sz  = this.el.getSize();
20932         
20933         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20934         this.el.select('.fc-day-content div',true).setHeight(34);
20935     },
20936     
20937     
20938     // private
20939     showPrevMonth : function(e){
20940         this.update(this.activeDate.add("mo", -1));
20941     },
20942     showToday : function(e){
20943         this.update(new Date().clearTime());
20944     },
20945     // private
20946     showNextMonth : function(e){
20947         this.update(this.activeDate.add("mo", 1));
20948     },
20949
20950     // private
20951     showPrevYear : function(){
20952         this.update(this.activeDate.add("y", -1));
20953     },
20954
20955     // private
20956     showNextYear : function(){
20957         this.update(this.activeDate.add("y", 1));
20958     },
20959
20960     
20961    // private
20962     update : function(date)
20963     {
20964         var vd = this.activeDate;
20965         this.activeDate = date;
20966 //        if(vd && this.el){
20967 //            var t = date.getTime();
20968 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20969 //                Roo.log('using add remove');
20970 //                
20971 //                this.fireEvent('monthchange', this, date);
20972 //                
20973 //                this.cells.removeClass("fc-state-highlight");
20974 //                this.cells.each(function(c){
20975 //                   if(c.dateValue == t){
20976 //                       c.addClass("fc-state-highlight");
20977 //                       setTimeout(function(){
20978 //                            try{c.dom.firstChild.focus();}catch(e){}
20979 //                       }, 50);
20980 //                       return false;
20981 //                   }
20982 //                   return true;
20983 //                });
20984 //                return;
20985 //            }
20986 //        }
20987         
20988         var days = date.getDaysInMonth();
20989         
20990         var firstOfMonth = date.getFirstDateOfMonth();
20991         var startingPos = firstOfMonth.getDay()-this.startDay;
20992         
20993         if(startingPos < this.startDay){
20994             startingPos += 7;
20995         }
20996         
20997         var pm = date.add(Date.MONTH, -1);
20998         var prevStart = pm.getDaysInMonth()-startingPos;
20999 //        
21000         this.cells = this.el.select('.fc-day',true);
21001         this.textNodes = this.el.query('.fc-day-number');
21002         this.cells.addClassOnOver('fc-state-hover');
21003         
21004         var cells = this.cells.elements;
21005         var textEls = this.textNodes;
21006         
21007         Roo.each(cells, function(cell){
21008             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21009         });
21010         
21011         days += startingPos;
21012
21013         // convert everything to numbers so it's fast
21014         var day = 86400000;
21015         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21016         //Roo.log(d);
21017         //Roo.log(pm);
21018         //Roo.log(prevStart);
21019         
21020         var today = new Date().clearTime().getTime();
21021         var sel = date.clearTime().getTime();
21022         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21023         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21024         var ddMatch = this.disabledDatesRE;
21025         var ddText = this.disabledDatesText;
21026         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21027         var ddaysText = this.disabledDaysText;
21028         var format = this.format;
21029         
21030         var setCellClass = function(cal, cell){
21031             cell.row = 0;
21032             cell.events = [];
21033             cell.more = [];
21034             //Roo.log('set Cell Class');
21035             cell.title = "";
21036             var t = d.getTime();
21037             
21038             //Roo.log(d);
21039             
21040             cell.dateValue = t;
21041             if(t == today){
21042                 cell.className += " fc-today";
21043                 cell.className += " fc-state-highlight";
21044                 cell.title = cal.todayText;
21045             }
21046             if(t == sel){
21047                 // disable highlight in other month..
21048                 //cell.className += " fc-state-highlight";
21049                 
21050             }
21051             // disabling
21052             if(t < min) {
21053                 cell.className = " fc-state-disabled";
21054                 cell.title = cal.minText;
21055                 return;
21056             }
21057             if(t > max) {
21058                 cell.className = " fc-state-disabled";
21059                 cell.title = cal.maxText;
21060                 return;
21061             }
21062             if(ddays){
21063                 if(ddays.indexOf(d.getDay()) != -1){
21064                     cell.title = ddaysText;
21065                     cell.className = " fc-state-disabled";
21066                 }
21067             }
21068             if(ddMatch && format){
21069                 var fvalue = d.dateFormat(format);
21070                 if(ddMatch.test(fvalue)){
21071                     cell.title = ddText.replace("%0", fvalue);
21072                     cell.className = " fc-state-disabled";
21073                 }
21074             }
21075             
21076             if (!cell.initialClassName) {
21077                 cell.initialClassName = cell.dom.className;
21078             }
21079             
21080             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21081         };
21082
21083         var i = 0;
21084         
21085         for(; i < startingPos; i++) {
21086             textEls[i].innerHTML = (++prevStart);
21087             d.setDate(d.getDate()+1);
21088             
21089             cells[i].className = "fc-past fc-other-month";
21090             setCellClass(this, cells[i]);
21091         }
21092         
21093         var intDay = 0;
21094         
21095         for(; i < days; i++){
21096             intDay = i - startingPos + 1;
21097             textEls[i].innerHTML = (intDay);
21098             d.setDate(d.getDate()+1);
21099             
21100             cells[i].className = ''; // "x-date-active";
21101             setCellClass(this, cells[i]);
21102         }
21103         var extraDays = 0;
21104         
21105         for(; i < 42; i++) {
21106             textEls[i].innerHTML = (++extraDays);
21107             d.setDate(d.getDate()+1);
21108             
21109             cells[i].className = "fc-future fc-other-month";
21110             setCellClass(this, cells[i]);
21111         }
21112         
21113         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21114         
21115         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21116         
21117         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21118         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21119         
21120         if(totalRows != 6){
21121             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21122             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21123         }
21124         
21125         this.fireEvent('monthchange', this, date);
21126         
21127         
21128         /*
21129         if(!this.internalRender){
21130             var main = this.el.dom.firstChild;
21131             var w = main.offsetWidth;
21132             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21133             Roo.fly(main).setWidth(w);
21134             this.internalRender = true;
21135             // opera does not respect the auto grow header center column
21136             // then, after it gets a width opera refuses to recalculate
21137             // without a second pass
21138             if(Roo.isOpera && !this.secondPass){
21139                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21140                 this.secondPass = true;
21141                 this.update.defer(10, this, [date]);
21142             }
21143         }
21144         */
21145         
21146     },
21147     
21148     findCell : function(dt) {
21149         dt = dt.clearTime().getTime();
21150         var ret = false;
21151         this.cells.each(function(c){
21152             //Roo.log("check " +c.dateValue + '?=' + dt);
21153             if(c.dateValue == dt){
21154                 ret = c;
21155                 return false;
21156             }
21157             return true;
21158         });
21159         
21160         return ret;
21161     },
21162     
21163     findCells : function(ev) {
21164         var s = ev.start.clone().clearTime().getTime();
21165        // Roo.log(s);
21166         var e= ev.end.clone().clearTime().getTime();
21167        // Roo.log(e);
21168         var ret = [];
21169         this.cells.each(function(c){
21170              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21171             
21172             if(c.dateValue > e){
21173                 return ;
21174             }
21175             if(c.dateValue < s){
21176                 return ;
21177             }
21178             ret.push(c);
21179         });
21180         
21181         return ret;    
21182     },
21183     
21184 //    findBestRow: function(cells)
21185 //    {
21186 //        var ret = 0;
21187 //        
21188 //        for (var i =0 ; i < cells.length;i++) {
21189 //            ret  = Math.max(cells[i].rows || 0,ret);
21190 //        }
21191 //        return ret;
21192 //        
21193 //    },
21194     
21195     
21196     addItem : function(ev)
21197     {
21198         // look for vertical location slot in
21199         var cells = this.findCells(ev);
21200         
21201 //        ev.row = this.findBestRow(cells);
21202         
21203         // work out the location.
21204         
21205         var crow = false;
21206         var rows = [];
21207         for(var i =0; i < cells.length; i++) {
21208             
21209             cells[i].row = cells[0].row;
21210             
21211             if(i == 0){
21212                 cells[i].row = cells[i].row + 1;
21213             }
21214             
21215             if (!crow) {
21216                 crow = {
21217                     start : cells[i],
21218                     end :  cells[i]
21219                 };
21220                 continue;
21221             }
21222             if (crow.start.getY() == cells[i].getY()) {
21223                 // on same row.
21224                 crow.end = cells[i];
21225                 continue;
21226             }
21227             // different row.
21228             rows.push(crow);
21229             crow = {
21230                 start: cells[i],
21231                 end : cells[i]
21232             };
21233             
21234         }
21235         
21236         rows.push(crow);
21237         ev.els = [];
21238         ev.rows = rows;
21239         ev.cells = cells;
21240         
21241         cells[0].events.push(ev);
21242         
21243         this.calevents.push(ev);
21244     },
21245     
21246     clearEvents: function() {
21247         
21248         if(!this.calevents){
21249             return;
21250         }
21251         
21252         Roo.each(this.cells.elements, function(c){
21253             c.row = 0;
21254             c.events = [];
21255             c.more = [];
21256         });
21257         
21258         Roo.each(this.calevents, function(e) {
21259             Roo.each(e.els, function(el) {
21260                 el.un('mouseenter' ,this.onEventEnter, this);
21261                 el.un('mouseleave' ,this.onEventLeave, this);
21262                 el.remove();
21263             },this);
21264         },this);
21265         
21266         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21267             e.remove();
21268         });
21269         
21270     },
21271     
21272     renderEvents: function()
21273     {   
21274         var _this = this;
21275         
21276         this.cells.each(function(c) {
21277             
21278             if(c.row < 5){
21279                 return;
21280             }
21281             
21282             var ev = c.events;
21283             
21284             var r = 4;
21285             if(c.row != c.events.length){
21286                 r = 4 - (4 - (c.row - c.events.length));
21287             }
21288             
21289             c.events = ev.slice(0, r);
21290             c.more = ev.slice(r);
21291             
21292             if(c.more.length && c.more.length == 1){
21293                 c.events.push(c.more.pop());
21294             }
21295             
21296             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21297             
21298         });
21299             
21300         this.cells.each(function(c) {
21301             
21302             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21303             
21304             
21305             for (var e = 0; e < c.events.length; e++){
21306                 var ev = c.events[e];
21307                 var rows = ev.rows;
21308                 
21309                 for(var i = 0; i < rows.length; i++) {
21310                 
21311                     // how many rows should it span..
21312
21313                     var  cfg = {
21314                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21315                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21316
21317                         unselectable : "on",
21318                         cn : [
21319                             {
21320                                 cls: 'fc-event-inner',
21321                                 cn : [
21322     //                                {
21323     //                                  tag:'span',
21324     //                                  cls: 'fc-event-time',
21325     //                                  html : cells.length > 1 ? '' : ev.time
21326     //                                },
21327                                     {
21328                                       tag:'span',
21329                                       cls: 'fc-event-title',
21330                                       html : String.format('{0}', ev.title)
21331                                     }
21332
21333
21334                                 ]
21335                             },
21336                             {
21337                                 cls: 'ui-resizable-handle ui-resizable-e',
21338                                 html : '&nbsp;&nbsp;&nbsp'
21339                             }
21340
21341                         ]
21342                     };
21343
21344                     if (i == 0) {
21345                         cfg.cls += ' fc-event-start';
21346                     }
21347                     if ((i+1) == rows.length) {
21348                         cfg.cls += ' fc-event-end';
21349                     }
21350
21351                     var ctr = _this.el.select('.fc-event-container',true).first();
21352                     var cg = ctr.createChild(cfg);
21353
21354                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21355                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21356
21357                     var r = (c.more.length) ? 1 : 0;
21358                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21359                     cg.setWidth(ebox.right - sbox.x -2);
21360
21361                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21362                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21363                     cg.on('click', _this.onEventClick, _this, ev);
21364
21365                     ev.els.push(cg);
21366                     
21367                 }
21368                 
21369             }
21370             
21371             
21372             if(c.more.length){
21373                 var  cfg = {
21374                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21375                     style : 'position: absolute',
21376                     unselectable : "on",
21377                     cn : [
21378                         {
21379                             cls: 'fc-event-inner',
21380                             cn : [
21381                                 {
21382                                   tag:'span',
21383                                   cls: 'fc-event-title',
21384                                   html : 'More'
21385                                 }
21386
21387
21388                             ]
21389                         },
21390                         {
21391                             cls: 'ui-resizable-handle ui-resizable-e',
21392                             html : '&nbsp;&nbsp;&nbsp'
21393                         }
21394
21395                     ]
21396                 };
21397
21398                 var ctr = _this.el.select('.fc-event-container',true).first();
21399                 var cg = ctr.createChild(cfg);
21400
21401                 var sbox = c.select('.fc-day-content',true).first().getBox();
21402                 var ebox = c.select('.fc-day-content',true).first().getBox();
21403                 //Roo.log(cg);
21404                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21405                 cg.setWidth(ebox.right - sbox.x -2);
21406
21407                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21408                 
21409             }
21410             
21411         });
21412         
21413         
21414         
21415     },
21416     
21417     onEventEnter: function (e, el,event,d) {
21418         this.fireEvent('evententer', this, el, event);
21419     },
21420     
21421     onEventLeave: function (e, el,event,d) {
21422         this.fireEvent('eventleave', this, el, event);
21423     },
21424     
21425     onEventClick: function (e, el,event,d) {
21426         this.fireEvent('eventclick', this, el, event);
21427     },
21428     
21429     onMonthChange: function () {
21430         this.store.load();
21431     },
21432     
21433     onMoreEventClick: function(e, el, more)
21434     {
21435         var _this = this;
21436         
21437         this.calpopover.placement = 'right';
21438         this.calpopover.setTitle('More');
21439         
21440         this.calpopover.setContent('');
21441         
21442         var ctr = this.calpopover.el.select('.popover-content', true).first();
21443         
21444         Roo.each(more, function(m){
21445             var cfg = {
21446                 cls : 'fc-event-hori fc-event-draggable',
21447                 html : m.title
21448             };
21449             var cg = ctr.createChild(cfg);
21450             
21451             cg.on('click', _this.onEventClick, _this, m);
21452         });
21453         
21454         this.calpopover.show(el);
21455         
21456         
21457     },
21458     
21459     onLoad: function () 
21460     {   
21461         this.calevents = [];
21462         var cal = this;
21463         
21464         if(this.store.getCount() > 0){
21465             this.store.data.each(function(d){
21466                cal.addItem({
21467                     id : d.data.id,
21468                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21469                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21470                     time : d.data.start_time,
21471                     title : d.data.title,
21472                     description : d.data.description,
21473                     venue : d.data.venue
21474                 });
21475             });
21476         }
21477         
21478         this.renderEvents();
21479         
21480         if(this.calevents.length && this.loadMask){
21481             this.maskEl.hide();
21482         }
21483     },
21484     
21485     onBeforeLoad: function()
21486     {
21487         this.clearEvents();
21488         if(this.loadMask){
21489             this.maskEl.show();
21490         }
21491     }
21492 });
21493
21494  
21495  /*
21496  * - LGPL
21497  *
21498  * element
21499  * 
21500  */
21501
21502 /**
21503  * @class Roo.bootstrap.Popover
21504  * @extends Roo.bootstrap.Component
21505  * @parent none builder
21506  * @children Roo.bootstrap.Component
21507  * Bootstrap Popover class
21508  * @cfg {String} html contents of the popover   (or false to use children..)
21509  * @cfg {String} title of popover (or false to hide)
21510  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21511  * @cfg {String} trigger click || hover (or false to trigger manually)
21512  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21513  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21514  *      - if false and it has a 'parent' then it will be automatically added to that element
21515  *      - if string - Roo.get  will be called 
21516  * @cfg {Number} delay - delay before showing
21517  
21518  * @constructor
21519  * Create a new Popover
21520  * @param {Object} config The config object
21521  */
21522
21523 Roo.bootstrap.Popover = function(config){
21524     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21525     
21526     this.addEvents({
21527         // raw events
21528          /**
21529          * @event show
21530          * After the popover show
21531          * 
21532          * @param {Roo.bootstrap.Popover} this
21533          */
21534         "show" : true,
21535         /**
21536          * @event hide
21537          * After the popover hide
21538          * 
21539          * @param {Roo.bootstrap.Popover} this
21540          */
21541         "hide" : true
21542     });
21543 };
21544
21545 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21546     
21547     title: false,
21548     html: false,
21549     
21550     placement : 'right',
21551     trigger : 'hover', // hover
21552     modal : false,
21553     delay : 0,
21554     
21555     over: false,
21556     
21557     can_build_overlaid : false,
21558     
21559     maskEl : false, // the mask element
21560     headerEl : false,
21561     contentEl : false,
21562     alignEl : false, // when show is called with an element - this get's stored.
21563     
21564     getChildContainer : function()
21565     {
21566         return this.contentEl;
21567         
21568     },
21569     getPopoverHeader : function()
21570     {
21571         this.title = true; // flag not to hide it..
21572         this.headerEl.addClass('p-0');
21573         return this.headerEl
21574     },
21575     
21576     
21577     getAutoCreate : function(){
21578          
21579         var cfg = {
21580            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21581            style: 'display:block',
21582            cn : [
21583                 {
21584                     cls : 'arrow'
21585                 },
21586                 {
21587                     cls : 'popover-inner ',
21588                     cn : [
21589                         {
21590                             tag: 'h3',
21591                             cls: 'popover-title popover-header',
21592                             html : this.title === false ? '' : this.title
21593                         },
21594                         {
21595                             cls : 'popover-content popover-body '  + (this.cls || ''),
21596                             html : this.html || ''
21597                         }
21598                     ]
21599                     
21600                 }
21601            ]
21602         };
21603         
21604         return cfg;
21605     },
21606     /**
21607      * @param {string} the title
21608      */
21609     setTitle: function(str)
21610     {
21611         this.title = str;
21612         if (this.el) {
21613             this.headerEl.dom.innerHTML = str;
21614         }
21615         
21616     },
21617     /**
21618      * @param {string} the body content
21619      */
21620     setContent: function(str)
21621     {
21622         this.html = str;
21623         if (this.contentEl) {
21624             this.contentEl.dom.innerHTML = str;
21625         }
21626         
21627     },
21628     // as it get's added to the bottom of the page.
21629     onRender : function(ct, position)
21630     {
21631         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21632         
21633         
21634         
21635         if(!this.el){
21636             var cfg = Roo.apply({},  this.getAutoCreate());
21637             cfg.id = Roo.id();
21638             
21639             if (this.cls) {
21640                 cfg.cls += ' ' + this.cls;
21641             }
21642             if (this.style) {
21643                 cfg.style = this.style;
21644             }
21645             //Roo.log("adding to ");
21646             this.el = Roo.get(document.body).createChild(cfg, position);
21647 //            Roo.log(this.el);
21648         }
21649         
21650         this.contentEl = this.el.select('.popover-content',true).first();
21651         this.headerEl =  this.el.select('.popover-title',true).first();
21652         
21653         var nitems = [];
21654         if(typeof(this.items) != 'undefined'){
21655             var items = this.items;
21656             delete this.items;
21657
21658             for(var i =0;i < items.length;i++) {
21659                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21660             }
21661         }
21662
21663         this.items = nitems;
21664         
21665         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21666         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21667         
21668         
21669         
21670         this.initEvents();
21671     },
21672     
21673     resizeMask : function()
21674     {
21675         this.maskEl.setSize(
21676             Roo.lib.Dom.getViewWidth(true),
21677             Roo.lib.Dom.getViewHeight(true)
21678         );
21679     },
21680     
21681     initEvents : function()
21682     {
21683         
21684         if (!this.modal) { 
21685             Roo.bootstrap.Popover.register(this);
21686         }
21687          
21688         this.arrowEl = this.el.select('.arrow',true).first();
21689         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21690         this.el.enableDisplayMode('block');
21691         this.el.hide();
21692  
21693         
21694         if (this.over === false && !this.parent()) {
21695             return; 
21696         }
21697         if (this.triggers === false) {
21698             return;
21699         }
21700          
21701         // support parent
21702         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21703         var triggers = this.trigger ? this.trigger.split(' ') : [];
21704         Roo.each(triggers, function(trigger) {
21705         
21706             if (trigger == 'click') {
21707                 on_el.on('click', this.toggle, this);
21708             } else if (trigger != 'manual') {
21709                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21710                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21711       
21712                 on_el.on(eventIn  ,this.enter, this);
21713                 on_el.on(eventOut, this.leave, this);
21714             }
21715         }, this);
21716     },
21717     
21718     
21719     // private
21720     timeout : null,
21721     hoverState : null,
21722     
21723     toggle : function () {
21724         this.hoverState == 'in' ? this.leave() : this.enter();
21725     },
21726     
21727     enter : function () {
21728         
21729         clearTimeout(this.timeout);
21730     
21731         this.hoverState = 'in';
21732     
21733         if (!this.delay || !this.delay.show) {
21734             this.show();
21735             return;
21736         }
21737         var _t = this;
21738         this.timeout = setTimeout(function () {
21739             if (_t.hoverState == 'in') {
21740                 _t.show();
21741             }
21742         }, this.delay.show)
21743     },
21744     
21745     leave : function() {
21746         clearTimeout(this.timeout);
21747     
21748         this.hoverState = 'out';
21749     
21750         if (!this.delay || !this.delay.hide) {
21751             this.hide();
21752             return;
21753         }
21754         var _t = this;
21755         this.timeout = setTimeout(function () {
21756             if (_t.hoverState == 'out') {
21757                 _t.hide();
21758             }
21759         }, this.delay.hide)
21760     },
21761     
21762     /**
21763      * update the position of the dialog
21764      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21765      * 
21766      *
21767      */
21768     
21769     doAlign : function()
21770     {
21771         
21772         if (this.alignEl) {
21773             this.updatePosition(this.placement, true);
21774              
21775         } else {
21776             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21777             var es = this.el.getSize();
21778             var x = Roo.lib.Dom.getViewWidth()/2;
21779             var y = Roo.lib.Dom.getViewHeight()/2;
21780             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21781             
21782         }
21783
21784          
21785          
21786         
21787         
21788     },
21789     
21790     /**
21791      * Show the popover
21792      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21793      * @param {string} (left|right|top|bottom) position
21794      */
21795     show : function (on_el, placement)
21796     {
21797         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21798         on_el = on_el || false; // default to false
21799          
21800         if (!on_el) {
21801             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21802                 on_el = this.parent().el;
21803             } else if (this.over) {
21804                 on_el = Roo.get(this.over);
21805             }
21806             
21807         }
21808         
21809         this.alignEl = Roo.get( on_el );
21810
21811         if (!this.el) {
21812             this.render(document.body);
21813         }
21814         
21815         
21816          
21817         
21818         if (this.title === false) {
21819             this.headerEl.hide();
21820         }
21821         
21822        
21823         this.el.show();
21824         this.el.dom.style.display = 'block';
21825          
21826         this.doAlign();
21827         
21828         //var arrow = this.el.select('.arrow',true).first();
21829         //arrow.set(align[2], 
21830         
21831         this.el.addClass('in');
21832         
21833          
21834         
21835         this.hoverState = 'in';
21836         
21837         if (this.modal) {
21838             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21839             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21840             this.maskEl.dom.style.display = 'block';
21841             this.maskEl.addClass('show');
21842         }
21843         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21844  
21845         this.fireEvent('show', this);
21846         
21847     },
21848     /**
21849      * fire this manually after loading a grid in the table for example
21850      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21851      * @param {Boolean} try and move it if we cant get right position.
21852      */
21853     updatePosition : function(placement, try_move)
21854     {
21855         // allow for calling with no parameters
21856         placement = placement   ? placement :  this.placement;
21857         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21858         
21859         this.el.removeClass([
21860             'fade','top','bottom', 'left', 'right','in',
21861             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21862         ]);
21863         this.el.addClass(placement + ' bs-popover-' + placement);
21864         
21865         if (!this.alignEl ) {
21866             return false;
21867         }
21868         
21869         switch (placement) {
21870             case 'right':
21871                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21872                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21873                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21874                     //normal display... or moved up/down.
21875                     this.el.setXY(offset);
21876                     var xy = this.alignEl.getAnchorXY('tr', false);
21877                     xy[0]+=2;xy[1]+=5;
21878                     this.arrowEl.setXY(xy);
21879                     return true;
21880                 }
21881                 // continue through...
21882                 return this.updatePosition('left', false);
21883                 
21884             
21885             case 'left':
21886                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21887                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21888                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21889                     //normal display... or moved up/down.
21890                     this.el.setXY(offset);
21891                     var xy = this.alignEl.getAnchorXY('tl', false);
21892                     xy[0]-=10;xy[1]+=5; // << fix me
21893                     this.arrowEl.setXY(xy);
21894                     return true;
21895                 }
21896                 // call self...
21897                 return this.updatePosition('right', false);
21898             
21899             case 'top':
21900                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21901                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[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('t', false);
21906                     xy[1]-=10; // << fix me
21907                     this.arrowEl.setXY(xy);
21908                     return true;
21909                 }
21910                 // fall through
21911                return this.updatePosition('bottom', false);
21912             
21913             case 'bottom':
21914                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21915                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21916                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21917                     //normal display... or moved up/down.
21918                     this.el.setXY(offset);
21919                     var xy = this.alignEl.getAnchorXY('b', false);
21920                      xy[1]+=2; // << fix me
21921                     this.arrowEl.setXY(xy);
21922                     return true;
21923                 }
21924                 // fall through
21925                 return this.updatePosition('top', false);
21926                 
21927             
21928         }
21929         
21930         
21931         return false;
21932     },
21933     
21934     hide : function()
21935     {
21936         this.el.setXY([0,0]);
21937         this.el.removeClass('in');
21938         this.el.hide();
21939         this.hoverState = null;
21940         this.maskEl.hide(); // always..
21941         this.fireEvent('hide', this);
21942     }
21943     
21944 });
21945
21946
21947 Roo.apply(Roo.bootstrap.Popover, {
21948
21949     alignment : {
21950         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21951         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21952         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21953         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21954     },
21955     
21956     zIndex : 20001,
21957
21958     clickHander : false,
21959     
21960     
21961
21962     onMouseDown : function(e)
21963     {
21964         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21965             /// what is nothing is showing..
21966             this.hideAll();
21967         }
21968          
21969     },
21970     
21971     
21972     popups : [],
21973     
21974     register : function(popup)
21975     {
21976         if (!Roo.bootstrap.Popover.clickHandler) {
21977             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21978         }
21979         // hide other popups.
21980         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21981         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21982         this.hideAll(); //<< why?
21983         //this.popups.push(popup);
21984     },
21985     hideAll : function()
21986     {
21987         this.popups.forEach(function(p) {
21988             p.hide();
21989         });
21990     },
21991     onShow : function() {
21992         Roo.bootstrap.Popover.popups.push(this);
21993     },
21994     onHide : function() {
21995         Roo.bootstrap.Popover.popups.remove(this);
21996     } 
21997
21998 });
21999 /**
22000  * @class Roo.bootstrap.PopoverNav
22001  * @extends Roo.bootstrap.nav.Simplebar
22002  * @parent Roo.bootstrap.Popover
22003  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22004  * @licence LGPL
22005  * Bootstrap Popover header navigation class
22006  * FIXME? should this go under nav?
22007  *
22008  * 
22009  * @constructor
22010  * Create a new Popover Header Navigation 
22011  * @param {Object} config The config object
22012  */
22013
22014 Roo.bootstrap.PopoverNav = function(config){
22015     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22016 };
22017
22018 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22019     
22020     
22021     container_method : 'getPopoverHeader' 
22022     
22023      
22024     
22025     
22026    
22027 });
22028
22029  
22030
22031  /*
22032  * - LGPL
22033  *
22034  * Progress
22035  * 
22036  */
22037
22038 /**
22039  * @class Roo.bootstrap.Progress
22040  * @extends Roo.bootstrap.Component
22041  * @children Roo.bootstrap.ProgressBar
22042  * Bootstrap Progress class
22043  * @cfg {Boolean} striped striped of the progress bar
22044  * @cfg {Boolean} active animated of the progress bar
22045  * 
22046  * 
22047  * @constructor
22048  * Create a new Progress
22049  * @param {Object} config The config object
22050  */
22051
22052 Roo.bootstrap.Progress = function(config){
22053     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22054 };
22055
22056 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22057     
22058     striped : false,
22059     active: false,
22060     
22061     getAutoCreate : function(){
22062         var cfg = {
22063             tag: 'div',
22064             cls: 'progress'
22065         };
22066         
22067         
22068         if(this.striped){
22069             cfg.cls += ' progress-striped';
22070         }
22071       
22072         if(this.active){
22073             cfg.cls += ' active';
22074         }
22075         
22076         
22077         return cfg;
22078     }
22079    
22080 });
22081
22082  
22083
22084  /*
22085  * - LGPL
22086  *
22087  * ProgressBar
22088  * 
22089  */
22090
22091 /**
22092  * @class Roo.bootstrap.ProgressBar
22093  * @extends Roo.bootstrap.Component
22094  * Bootstrap ProgressBar class
22095  * @cfg {Number} aria_valuenow aria-value now
22096  * @cfg {Number} aria_valuemin aria-value min
22097  * @cfg {Number} aria_valuemax aria-value max
22098  * @cfg {String} label label for the progress bar
22099  * @cfg {String} panel (success | info | warning | danger )
22100  * @cfg {String} role role of the progress bar
22101  * @cfg {String} sr_only text
22102  * 
22103  * 
22104  * @constructor
22105  * Create a new ProgressBar
22106  * @param {Object} config The config object
22107  */
22108
22109 Roo.bootstrap.ProgressBar = function(config){
22110     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22111 };
22112
22113 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22114     
22115     aria_valuenow : 0,
22116     aria_valuemin : 0,
22117     aria_valuemax : 100,
22118     label : false,
22119     panel : false,
22120     role : false,
22121     sr_only: false,
22122     
22123     getAutoCreate : function()
22124     {
22125         
22126         var cfg = {
22127             tag: 'div',
22128             cls: 'progress-bar',
22129             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22130         };
22131         
22132         if(this.sr_only){
22133             cfg.cn = {
22134                 tag: 'span',
22135                 cls: 'sr-only',
22136                 html: this.sr_only
22137             }
22138         }
22139         
22140         if(this.role){
22141             cfg.role = this.role;
22142         }
22143         
22144         if(this.aria_valuenow){
22145             cfg['aria-valuenow'] = this.aria_valuenow;
22146         }
22147         
22148         if(this.aria_valuemin){
22149             cfg['aria-valuemin'] = this.aria_valuemin;
22150         }
22151         
22152         if(this.aria_valuemax){
22153             cfg['aria-valuemax'] = this.aria_valuemax;
22154         }
22155         
22156         if(this.label && !this.sr_only){
22157             cfg.html = this.label;
22158         }
22159         
22160         if(this.panel){
22161             cfg.cls += ' progress-bar-' + this.panel;
22162         }
22163         
22164         return cfg;
22165     },
22166     
22167     update : function(aria_valuenow)
22168     {
22169         this.aria_valuenow = aria_valuenow;
22170         
22171         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22172     }
22173    
22174 });
22175
22176  
22177
22178  /**
22179  * @class Roo.bootstrap.TabGroup
22180  * @extends Roo.bootstrap.Column
22181  * @children Roo.bootstrap.TabPanel
22182  * Bootstrap Column class
22183  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22184  * @cfg {Boolean} carousel true to make the group behave like a carousel
22185  * @cfg {Boolean} bullets show bullets for the panels
22186  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22187  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22188  * @cfg {Boolean} showarrow (true|false) show arrow default true
22189  * 
22190  * @constructor
22191  * Create a new TabGroup
22192  * @param {Object} config The config object
22193  */
22194
22195 Roo.bootstrap.TabGroup = function(config){
22196     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22197     if (!this.navId) {
22198         this.navId = Roo.id();
22199     }
22200     this.tabs = [];
22201     Roo.bootstrap.TabGroup.register(this);
22202     
22203 };
22204
22205 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22206     
22207     carousel : false,
22208     transition : false,
22209     bullets : 0,
22210     timer : 0,
22211     autoslide : false,
22212     slideFn : false,
22213     slideOnTouch : false,
22214     showarrow : true,
22215     
22216     getAutoCreate : function()
22217     {
22218         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22219         
22220         cfg.cls += ' tab-content';
22221         
22222         if (this.carousel) {
22223             cfg.cls += ' carousel slide';
22224             
22225             cfg.cn = [{
22226                cls : 'carousel-inner',
22227                cn : []
22228             }];
22229         
22230             if(this.bullets  && !Roo.isTouch){
22231                 
22232                 var bullets = {
22233                     cls : 'carousel-bullets',
22234                     cn : []
22235                 };
22236                
22237                 if(this.bullets_cls){
22238                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22239                 }
22240                 
22241                 bullets.cn.push({
22242                     cls : 'clear'
22243                 });
22244                 
22245                 cfg.cn[0].cn.push(bullets);
22246             }
22247             
22248             if(this.showarrow){
22249                 cfg.cn[0].cn.push({
22250                     tag : 'div',
22251                     class : 'carousel-arrow',
22252                     cn : [
22253                         {
22254                             tag : 'div',
22255                             class : 'carousel-prev',
22256                             cn : [
22257                                 {
22258                                     tag : 'i',
22259                                     class : 'fa fa-chevron-left'
22260                                 }
22261                             ]
22262                         },
22263                         {
22264                             tag : 'div',
22265                             class : 'carousel-next',
22266                             cn : [
22267                                 {
22268                                     tag : 'i',
22269                                     class : 'fa fa-chevron-right'
22270                                 }
22271                             ]
22272                         }
22273                     ]
22274                 });
22275             }
22276             
22277         }
22278         
22279         return cfg;
22280     },
22281     
22282     initEvents:  function()
22283     {
22284 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22285 //            this.el.on("touchstart", this.onTouchStart, this);
22286 //        }
22287         
22288         if(this.autoslide){
22289             var _this = this;
22290             
22291             this.slideFn = window.setInterval(function() {
22292                 _this.showPanelNext();
22293             }, this.timer);
22294         }
22295         
22296         if(this.showarrow){
22297             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22298             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22299         }
22300         
22301         
22302     },
22303     
22304 //    onTouchStart : function(e, el, o)
22305 //    {
22306 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22307 //            return;
22308 //        }
22309 //        
22310 //        this.showPanelNext();
22311 //    },
22312     
22313     
22314     getChildContainer : function()
22315     {
22316         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22317     },
22318     
22319     /**
22320     * register a Navigation item
22321     * @param {Roo.bootstrap.nav.Item} the navitem to add
22322     */
22323     register : function(item)
22324     {
22325         this.tabs.push( item);
22326         item.navId = this.navId; // not really needed..
22327         this.addBullet();
22328     
22329     },
22330     
22331     getActivePanel : function()
22332     {
22333         var r = false;
22334         Roo.each(this.tabs, function(t) {
22335             if (t.active) {
22336                 r = t;
22337                 return false;
22338             }
22339             return null;
22340         });
22341         return r;
22342         
22343     },
22344     getPanelByName : function(n)
22345     {
22346         var r = false;
22347         Roo.each(this.tabs, function(t) {
22348             if (t.tabId == n) {
22349                 r = t;
22350                 return false;
22351             }
22352             return null;
22353         });
22354         return r;
22355     },
22356     indexOfPanel : function(p)
22357     {
22358         var r = false;
22359         Roo.each(this.tabs, function(t,i) {
22360             if (t.tabId == p.tabId) {
22361                 r = i;
22362                 return false;
22363             }
22364             return null;
22365         });
22366         return r;
22367     },
22368     /**
22369      * show a specific panel
22370      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22371      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22372      */
22373     showPanel : function (pan)
22374     {
22375         if(this.transition || typeof(pan) == 'undefined'){
22376             Roo.log("waiting for the transitionend");
22377             return false;
22378         }
22379         
22380         if (typeof(pan) == 'number') {
22381             pan = this.tabs[pan];
22382         }
22383         
22384         if (typeof(pan) == 'string') {
22385             pan = this.getPanelByName(pan);
22386         }
22387         
22388         var cur = this.getActivePanel();
22389         
22390         if(!pan || !cur){
22391             Roo.log('pan or acitve pan is undefined');
22392             return false;
22393         }
22394         
22395         if (pan.tabId == this.getActivePanel().tabId) {
22396             return true;
22397         }
22398         
22399         if (false === cur.fireEvent('beforedeactivate')) {
22400             return false;
22401         }
22402         
22403         if(this.bullets > 0 && !Roo.isTouch){
22404             this.setActiveBullet(this.indexOfPanel(pan));
22405         }
22406         
22407         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22408             
22409             //class="carousel-item carousel-item-next carousel-item-left"
22410             
22411             this.transition = true;
22412             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22413             var lr = dir == 'next' ? 'left' : 'right';
22414             pan.el.addClass(dir); // or prev
22415             pan.el.addClass('carousel-item-' + dir); // or prev
22416             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22417             cur.el.addClass(lr); // or right
22418             pan.el.addClass(lr);
22419             cur.el.addClass('carousel-item-' +lr); // or right
22420             pan.el.addClass('carousel-item-' +lr);
22421             
22422             
22423             var _this = this;
22424             cur.el.on('transitionend', function() {
22425                 Roo.log("trans end?");
22426                 
22427                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22428                 pan.setActive(true);
22429                 
22430                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22431                 cur.setActive(false);
22432                 
22433                 _this.transition = false;
22434                 
22435             }, this, { single:  true } );
22436             
22437             return true;
22438         }
22439         
22440         cur.setActive(false);
22441         pan.setActive(true);
22442         
22443         return true;
22444         
22445     },
22446     showPanelNext : function()
22447     {
22448         var i = this.indexOfPanel(this.getActivePanel());
22449         
22450         if (i >= this.tabs.length - 1 && !this.autoslide) {
22451             return;
22452         }
22453         
22454         if (i >= this.tabs.length - 1 && this.autoslide) {
22455             i = -1;
22456         }
22457         
22458         this.showPanel(this.tabs[i+1]);
22459     },
22460     
22461     showPanelPrev : function()
22462     {
22463         var i = this.indexOfPanel(this.getActivePanel());
22464         
22465         if (i  < 1 && !this.autoslide) {
22466             return;
22467         }
22468         
22469         if (i < 1 && this.autoslide) {
22470             i = this.tabs.length;
22471         }
22472         
22473         this.showPanel(this.tabs[i-1]);
22474     },
22475     
22476     
22477     addBullet: function()
22478     {
22479         if(!this.bullets || Roo.isTouch){
22480             return;
22481         }
22482         var ctr = this.el.select('.carousel-bullets',true).first();
22483         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22484         var bullet = ctr.createChild({
22485             cls : 'bullet bullet-' + i
22486         },ctr.dom.lastChild);
22487         
22488         
22489         var _this = this;
22490         
22491         bullet.on('click', (function(e, el, o, ii, t){
22492
22493             e.preventDefault();
22494
22495             this.showPanel(ii);
22496
22497             if(this.autoslide && this.slideFn){
22498                 clearInterval(this.slideFn);
22499                 this.slideFn = window.setInterval(function() {
22500                     _this.showPanelNext();
22501                 }, this.timer);
22502             }
22503
22504         }).createDelegate(this, [i, bullet], true));
22505                 
22506         
22507     },
22508      
22509     setActiveBullet : function(i)
22510     {
22511         if(Roo.isTouch){
22512             return;
22513         }
22514         
22515         Roo.each(this.el.select('.bullet', true).elements, function(el){
22516             el.removeClass('selected');
22517         });
22518
22519         var bullet = this.el.select('.bullet-' + i, true).first();
22520         
22521         if(!bullet){
22522             return;
22523         }
22524         
22525         bullet.addClass('selected');
22526     }
22527     
22528     
22529   
22530 });
22531
22532  
22533
22534  
22535  
22536 Roo.apply(Roo.bootstrap.TabGroup, {
22537     
22538     groups: {},
22539      /**
22540     * register a Navigation Group
22541     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22542     */
22543     register : function(navgrp)
22544     {
22545         this.groups[navgrp.navId] = navgrp;
22546         
22547     },
22548     /**
22549     * fetch a Navigation Group based on the navigation ID
22550     * if one does not exist , it will get created.
22551     * @param {string} the navgroup to add
22552     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22553     */
22554     get: function(navId) {
22555         if (typeof(this.groups[navId]) == 'undefined') {
22556             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22557         }
22558         return this.groups[navId] ;
22559     }
22560     
22561     
22562     
22563 });
22564
22565  /*
22566  * - LGPL
22567  *
22568  * TabPanel
22569  * 
22570  */
22571
22572 /**
22573  * @class Roo.bootstrap.TabPanel
22574  * @extends Roo.bootstrap.Component
22575  * @children Roo.bootstrap.Component
22576  * Bootstrap TabPanel class
22577  * @cfg {Boolean} active panel active
22578  * @cfg {String} html panel content
22579  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22580  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22581  * @cfg {String} href click to link..
22582  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22583  * 
22584  * 
22585  * @constructor
22586  * Create a new TabPanel
22587  * @param {Object} config The config object
22588  */
22589
22590 Roo.bootstrap.TabPanel = function(config){
22591     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22592     this.addEvents({
22593         /**
22594              * @event changed
22595              * Fires when the active status changes
22596              * @param {Roo.bootstrap.TabPanel} this
22597              * @param {Boolean} state the new state
22598             
22599          */
22600         'changed': true,
22601         /**
22602              * @event beforedeactivate
22603              * Fires before a tab is de-activated - can be used to do validation on a form.
22604              * @param {Roo.bootstrap.TabPanel} this
22605              * @return {Boolean} false if there is an error
22606             
22607          */
22608         'beforedeactivate': true
22609      });
22610     
22611     this.tabId = this.tabId || Roo.id();
22612   
22613 };
22614
22615 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22616     
22617     active: false,
22618     html: false,
22619     tabId: false,
22620     navId : false,
22621     href : '',
22622     touchSlide : false,
22623     getAutoCreate : function(){
22624         
22625         
22626         var cfg = {
22627             tag: 'div',
22628             // item is needed for carousel - not sure if it has any effect otherwise
22629             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22630             html: this.html || ''
22631         };
22632         
22633         if(this.active){
22634             cfg.cls += ' active';
22635         }
22636         
22637         if(this.tabId){
22638             cfg.tabId = this.tabId;
22639         }
22640         
22641         
22642         
22643         return cfg;
22644     },
22645     
22646     initEvents:  function()
22647     {
22648         var p = this.parent();
22649         
22650         this.navId = this.navId || p.navId;
22651         
22652         if (typeof(this.navId) != 'undefined') {
22653             // not really needed.. but just in case.. parent should be a NavGroup.
22654             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22655             
22656             tg.register(this);
22657             
22658             var i = tg.tabs.length - 1;
22659             
22660             if(this.active && tg.bullets > 0 && i < tg.bullets){
22661                 tg.setActiveBullet(i);
22662             }
22663         }
22664         
22665         this.el.on('click', this.onClick, this);
22666         
22667         if(Roo.isTouch && this.touchSlide){
22668             this.el.on("touchstart", this.onTouchStart, this);
22669             this.el.on("touchmove", this.onTouchMove, this);
22670             this.el.on("touchend", this.onTouchEnd, this);
22671         }
22672         
22673     },
22674     
22675     onRender : function(ct, position)
22676     {
22677         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22678     },
22679     
22680     setActive : function(state)
22681     {
22682         Roo.log("panel - set active " + this.tabId + "=" + state);
22683         
22684         this.active = state;
22685         if (!state) {
22686             this.el.removeClass('active');
22687             
22688         } else  if (!this.el.hasClass('active')) {
22689             this.el.addClass('active');
22690         }
22691         
22692         this.fireEvent('changed', this, state);
22693     },
22694     
22695     onClick : function(e)
22696     {
22697         e.preventDefault();
22698         
22699         if(!this.href.length){
22700             return;
22701         }
22702         
22703         window.location.href = this.href;
22704     },
22705     
22706     startX : 0,
22707     startY : 0,
22708     endX : 0,
22709     endY : 0,
22710     swiping : false,
22711     
22712     onTouchStart : function(e)
22713     {
22714         this.swiping = false;
22715         
22716         this.startX = e.browserEvent.touches[0].clientX;
22717         this.startY = e.browserEvent.touches[0].clientY;
22718     },
22719     
22720     onTouchMove : function(e)
22721     {
22722         this.swiping = true;
22723         
22724         this.endX = e.browserEvent.touches[0].clientX;
22725         this.endY = e.browserEvent.touches[0].clientY;
22726     },
22727     
22728     onTouchEnd : function(e)
22729     {
22730         if(!this.swiping){
22731             this.onClick(e);
22732             return;
22733         }
22734         
22735         var tabGroup = this.parent();
22736         
22737         if(this.endX > this.startX){ // swiping right
22738             tabGroup.showPanelPrev();
22739             return;
22740         }
22741         
22742         if(this.startX > this.endX){ // swiping left
22743             tabGroup.showPanelNext();
22744             return;
22745         }
22746     }
22747     
22748     
22749 });
22750  
22751
22752  
22753
22754  /*
22755  * - LGPL
22756  *
22757  * DateField
22758  * 
22759  */
22760
22761 /**
22762  * @class Roo.bootstrap.form.DateField
22763  * @extends Roo.bootstrap.form.Input
22764  * Bootstrap DateField class
22765  * @cfg {Number} weekStart default 0
22766  * @cfg {String} viewMode default empty, (months|years)
22767  * @cfg {String} minViewMode default empty, (months|years)
22768  * @cfg {Number} startDate default -Infinity
22769  * @cfg {Number} endDate default Infinity
22770  * @cfg {Boolean} todayHighlight default false
22771  * @cfg {Boolean} todayBtn default false
22772  * @cfg {Boolean} calendarWeeks default false
22773  * @cfg {Object} daysOfWeekDisabled default empty
22774  * @cfg {Boolean} singleMode default false (true | false)
22775  * 
22776  * @cfg {Boolean} keyboardNavigation default true
22777  * @cfg {String} language default en
22778  * 
22779  * @constructor
22780  * Create a new DateField
22781  * @param {Object} config The config object
22782  */
22783
22784 Roo.bootstrap.form.DateField = function(config){
22785     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22786      this.addEvents({
22787             /**
22788              * @event show
22789              * Fires when this field show.
22790              * @param {Roo.bootstrap.form.DateField} this
22791              * @param {Mixed} date The date value
22792              */
22793             show : true,
22794             /**
22795              * @event show
22796              * Fires when this field hide.
22797              * @param {Roo.bootstrap.form.DateField} this
22798              * @param {Mixed} date The date value
22799              */
22800             hide : true,
22801             /**
22802              * @event select
22803              * Fires when select a date.
22804              * @param {Roo.bootstrap.form.DateField} this
22805              * @param {Mixed} date The date value
22806              */
22807             select : true,
22808             /**
22809              * @event beforeselect
22810              * Fires when before select a date.
22811              * @param {Roo.bootstrap.form.DateField} this
22812              * @param {Mixed} date The date value
22813              */
22814             beforeselect : true
22815         });
22816 };
22817
22818 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22819     
22820     /**
22821      * @cfg {String} format
22822      * The default date format string which can be overriden for localization support.  The format must be
22823      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22824      */
22825     format : "m/d/y",
22826     /**
22827      * @cfg {String} altFormats
22828      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22829      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22830      */
22831     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22832     
22833     weekStart : 0,
22834     
22835     viewMode : '',
22836     
22837     minViewMode : '',
22838     
22839     todayHighlight : false,
22840     
22841     todayBtn: false,
22842     
22843     language: 'en',
22844     
22845     keyboardNavigation: true,
22846     
22847     calendarWeeks: false,
22848     
22849     startDate: -Infinity,
22850     
22851     endDate: Infinity,
22852     
22853     daysOfWeekDisabled: [],
22854     
22855     _events: [],
22856     
22857     singleMode : false,
22858     
22859     UTCDate: function()
22860     {
22861         return new Date(Date.UTC.apply(Date, arguments));
22862     },
22863     
22864     UTCToday: function()
22865     {
22866         var today = new Date();
22867         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22868     },
22869     
22870     getDate: function() {
22871             var d = this.getUTCDate();
22872             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22873     },
22874     
22875     getUTCDate: function() {
22876             return this.date;
22877     },
22878     
22879     setDate: function(d) {
22880             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22881     },
22882     
22883     setUTCDate: function(d) {
22884             this.date = d;
22885             this.setValue(this.formatDate(this.date));
22886     },
22887         
22888     onRender: function(ct, position)
22889     {
22890         
22891         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22892         
22893         this.language = this.language || 'en';
22894         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22895         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22896         
22897         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22898         this.format = this.format || 'm/d/y';
22899         this.isInline = false;
22900         this.isInput = true;
22901         this.component = this.el.select('.add-on', true).first() || false;
22902         this.component = (this.component && this.component.length === 0) ? false : this.component;
22903         this.hasInput = this.component && this.inputEl().length;
22904         
22905         if (typeof(this.minViewMode === 'string')) {
22906             switch (this.minViewMode) {
22907                 case 'months':
22908                     this.minViewMode = 1;
22909                     break;
22910                 case 'years':
22911                     this.minViewMode = 2;
22912                     break;
22913                 default:
22914                     this.minViewMode = 0;
22915                     break;
22916             }
22917         }
22918         
22919         if (typeof(this.viewMode === 'string')) {
22920             switch (this.viewMode) {
22921                 case 'months':
22922                     this.viewMode = 1;
22923                     break;
22924                 case 'years':
22925                     this.viewMode = 2;
22926                     break;
22927                 default:
22928                     this.viewMode = 0;
22929                     break;
22930             }
22931         }
22932                 
22933         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22934         
22935 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22936         
22937         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22938         
22939         this.picker().on('mousedown', this.onMousedown, this);
22940         this.picker().on('click', this.onClick, this);
22941         
22942         this.picker().addClass('datepicker-dropdown');
22943         
22944         this.startViewMode = this.viewMode;
22945         
22946         if(this.singleMode){
22947             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22948                 v.setVisibilityMode(Roo.Element.DISPLAY);
22949                 v.hide();
22950             });
22951             
22952             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22953                 v.setStyle('width', '189px');
22954             });
22955         }
22956         
22957         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22958             if(!this.calendarWeeks){
22959                 v.remove();
22960                 return;
22961             }
22962             
22963             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22964             v.attr('colspan', function(i, val){
22965                 return parseInt(val) + 1;
22966             });
22967         });
22968                         
22969         
22970         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22971         
22972         this.setStartDate(this.startDate);
22973         this.setEndDate(this.endDate);
22974         
22975         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22976         
22977         this.fillDow();
22978         this.fillMonths();
22979         this.update();
22980         this.showMode();
22981         
22982         if(this.isInline) {
22983             this.showPopup();
22984         }
22985     },
22986     
22987     picker : function()
22988     {
22989         return this.pickerEl;
22990 //        return this.el.select('.datepicker', true).first();
22991     },
22992     
22993     fillDow: function()
22994     {
22995         var dowCnt = this.weekStart;
22996         
22997         var dow = {
22998             tag: 'tr',
22999             cn: [
23000                 
23001             ]
23002         };
23003         
23004         if(this.calendarWeeks){
23005             dow.cn.push({
23006                 tag: 'th',
23007                 cls: 'cw',
23008                 html: '&nbsp;'
23009             })
23010         }
23011         
23012         while (dowCnt < this.weekStart + 7) {
23013             dow.cn.push({
23014                 tag: 'th',
23015                 cls: 'dow',
23016                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23017             });
23018         }
23019         
23020         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23021     },
23022     
23023     fillMonths: function()
23024     {    
23025         var i = 0;
23026         var months = this.picker().select('>.datepicker-months td', true).first();
23027         
23028         months.dom.innerHTML = '';
23029         
23030         while (i < 12) {
23031             var month = {
23032                 tag: 'span',
23033                 cls: 'month',
23034                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23035             };
23036             
23037             months.createChild(month);
23038         }
23039         
23040     },
23041     
23042     update: function()
23043     {
23044         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;
23045         
23046         if (this.date < this.startDate) {
23047             this.viewDate = new Date(this.startDate);
23048         } else if (this.date > this.endDate) {
23049             this.viewDate = new Date(this.endDate);
23050         } else {
23051             this.viewDate = new Date(this.date);
23052         }
23053         
23054         this.fill();
23055     },
23056     
23057     fill: function() 
23058     {
23059         var d = new Date(this.viewDate),
23060                 year = d.getUTCFullYear(),
23061                 month = d.getUTCMonth(),
23062                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23063                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23064                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23065                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23066                 currentDate = this.date && this.date.valueOf(),
23067                 today = this.UTCToday();
23068         
23069         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23070         
23071 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23072         
23073 //        this.picker.select('>tfoot th.today').
23074 //                                              .text(dates[this.language].today)
23075 //                                              .toggle(this.todayBtn !== false);
23076     
23077         this.updateNavArrows();
23078         this.fillMonths();
23079                                                 
23080         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23081         
23082         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23083          
23084         prevMonth.setUTCDate(day);
23085         
23086         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23087         
23088         var nextMonth = new Date(prevMonth);
23089         
23090         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23091         
23092         nextMonth = nextMonth.valueOf();
23093         
23094         var fillMonths = false;
23095         
23096         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23097         
23098         while(prevMonth.valueOf() <= nextMonth) {
23099             var clsName = '';
23100             
23101             if (prevMonth.getUTCDay() === this.weekStart) {
23102                 if(fillMonths){
23103                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23104                 }
23105                     
23106                 fillMonths = {
23107                     tag: 'tr',
23108                     cn: []
23109                 };
23110                 
23111                 if(this.calendarWeeks){
23112                     // ISO 8601: First week contains first thursday.
23113                     // ISO also states week starts on Monday, but we can be more abstract here.
23114                     var
23115                     // Start of current week: based on weekstart/current date
23116                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23117                     // Thursday of this week
23118                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23119                     // First Thursday of year, year from thursday
23120                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23121                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23122                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23123                     
23124                     fillMonths.cn.push({
23125                         tag: 'td',
23126                         cls: 'cw',
23127                         html: calWeek
23128                     });
23129                 }
23130             }
23131             
23132             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23133                 clsName += ' old';
23134             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23135                 clsName += ' new';
23136             }
23137             if (this.todayHighlight &&
23138                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23139                 prevMonth.getUTCMonth() == today.getMonth() &&
23140                 prevMonth.getUTCDate() == today.getDate()) {
23141                 clsName += ' today';
23142             }
23143             
23144             if (currentDate && prevMonth.valueOf() === currentDate) {
23145                 clsName += ' active';
23146             }
23147             
23148             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23149                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23150                     clsName += ' disabled';
23151             }
23152             
23153             fillMonths.cn.push({
23154                 tag: 'td',
23155                 cls: 'day ' + clsName,
23156                 html: prevMonth.getDate()
23157             });
23158             
23159             prevMonth.setDate(prevMonth.getDate()+1);
23160         }
23161           
23162         var currentYear = this.date && this.date.getUTCFullYear();
23163         var currentMonth = this.date && this.date.getUTCMonth();
23164         
23165         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23166         
23167         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23168             v.removeClass('active');
23169             
23170             if(currentYear === year && k === currentMonth){
23171                 v.addClass('active');
23172             }
23173             
23174             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23175                 v.addClass('disabled');
23176             }
23177             
23178         });
23179         
23180         
23181         year = parseInt(year/10, 10) * 10;
23182         
23183         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23184         
23185         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23186         
23187         year -= 1;
23188         for (var i = -1; i < 11; i++) {
23189             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23190                 tag: 'span',
23191                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23192                 html: year
23193             });
23194             
23195             year += 1;
23196         }
23197     },
23198     
23199     showMode: function(dir) 
23200     {
23201         if (dir) {
23202             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23203         }
23204         
23205         Roo.each(this.picker().select('>div',true).elements, function(v){
23206             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23207             v.hide();
23208         });
23209         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23210     },
23211     
23212     place: function()
23213     {
23214         if(this.isInline) {
23215             return;
23216         }
23217         
23218         this.picker().removeClass(['bottom', 'top']);
23219         
23220         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23221             /*
23222              * place to the top of element!
23223              *
23224              */
23225             
23226             this.picker().addClass('top');
23227             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23228             
23229             return;
23230         }
23231         
23232         this.picker().addClass('bottom');
23233         
23234         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23235     },
23236     
23237     parseDate : function(value)
23238     {
23239         if(!value || value instanceof Date){
23240             return value;
23241         }
23242         var v = Date.parseDate(value, this.format);
23243         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23244             v = Date.parseDate(value, 'Y-m-d');
23245         }
23246         if(!v && this.altFormats){
23247             if(!this.altFormatsArray){
23248                 this.altFormatsArray = this.altFormats.split("|");
23249             }
23250             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23251                 v = Date.parseDate(value, this.altFormatsArray[i]);
23252             }
23253         }
23254         return v;
23255     },
23256     
23257     formatDate : function(date, fmt)
23258     {   
23259         return (!date || !(date instanceof Date)) ?
23260         date : date.dateFormat(fmt || this.format);
23261     },
23262     
23263     onFocus : function()
23264     {
23265         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23266         this.showPopup();
23267     },
23268     
23269     onBlur : function()
23270     {
23271         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23272         
23273         var d = this.inputEl().getValue();
23274         
23275         this.setValue(d);
23276                 
23277         this.hidePopup();
23278     },
23279     
23280     showPopup : function()
23281     {
23282         this.picker().show();
23283         this.update();
23284         this.place();
23285         
23286         this.fireEvent('showpopup', this, this.date);
23287     },
23288     
23289     hidePopup : function()
23290     {
23291         if(this.isInline) {
23292             return;
23293         }
23294         this.picker().hide();
23295         this.viewMode = this.startViewMode;
23296         this.showMode();
23297         
23298         this.fireEvent('hidepopup', this, this.date);
23299         
23300     },
23301     
23302     onMousedown: function(e)
23303     {
23304         e.stopPropagation();
23305         e.preventDefault();
23306     },
23307     
23308     keyup: function(e)
23309     {
23310         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23311         this.update();
23312     },
23313
23314     setValue: function(v)
23315     {
23316         if(this.fireEvent('beforeselect', this, v) !== false){
23317             var d = new Date(this.parseDate(v) ).clearTime();
23318         
23319             if(isNaN(d.getTime())){
23320                 this.date = this.viewDate = '';
23321                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23322                 return;
23323             }
23324
23325             v = this.formatDate(d);
23326
23327             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23328
23329             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23330
23331             this.update();
23332
23333             this.fireEvent('select', this, this.date);
23334         }
23335     },
23336     
23337     getValue: function()
23338     {
23339         return this.formatDate(this.date);
23340     },
23341     
23342     fireKey: function(e)
23343     {
23344         if (!this.picker().isVisible()){
23345             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23346                 this.showPopup();
23347             }
23348             return;
23349         }
23350         
23351         var dateChanged = false,
23352         dir, day, month,
23353         newDate, newViewDate;
23354         
23355         switch(e.keyCode){
23356             case 27: // escape
23357                 this.hidePopup();
23358                 e.preventDefault();
23359                 break;
23360             case 37: // left
23361             case 39: // right
23362                 if (!this.keyboardNavigation) {
23363                     break;
23364                 }
23365                 dir = e.keyCode == 37 ? -1 : 1;
23366                 
23367                 if (e.ctrlKey){
23368                     newDate = this.moveYear(this.date, dir);
23369                     newViewDate = this.moveYear(this.viewDate, dir);
23370                 } else if (e.shiftKey){
23371                     newDate = this.moveMonth(this.date, dir);
23372                     newViewDate = this.moveMonth(this.viewDate, dir);
23373                 } else {
23374                     newDate = new Date(this.date);
23375                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23376                     newViewDate = new Date(this.viewDate);
23377                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23378                 }
23379                 if (this.dateWithinRange(newDate)){
23380                     this.date = newDate;
23381                     this.viewDate = newViewDate;
23382                     this.setValue(this.formatDate(this.date));
23383 //                    this.update();
23384                     e.preventDefault();
23385                     dateChanged = true;
23386                 }
23387                 break;
23388             case 38: // up
23389             case 40: // down
23390                 if (!this.keyboardNavigation) {
23391                     break;
23392                 }
23393                 dir = e.keyCode == 38 ? -1 : 1;
23394                 if (e.ctrlKey){
23395                     newDate = this.moveYear(this.date, dir);
23396                     newViewDate = this.moveYear(this.viewDate, dir);
23397                 } else if (e.shiftKey){
23398                     newDate = this.moveMonth(this.date, dir);
23399                     newViewDate = this.moveMonth(this.viewDate, dir);
23400                 } else {
23401                     newDate = new Date(this.date);
23402                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23403                     newViewDate = new Date(this.viewDate);
23404                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23405                 }
23406                 if (this.dateWithinRange(newDate)){
23407                     this.date = newDate;
23408                     this.viewDate = newViewDate;
23409                     this.setValue(this.formatDate(this.date));
23410 //                    this.update();
23411                     e.preventDefault();
23412                     dateChanged = true;
23413                 }
23414                 break;
23415             case 13: // enter
23416                 this.setValue(this.formatDate(this.date));
23417                 this.hidePopup();
23418                 e.preventDefault();
23419                 break;
23420             case 9: // tab
23421                 this.setValue(this.formatDate(this.date));
23422                 this.hidePopup();
23423                 break;
23424             case 16: // shift
23425             case 17: // ctrl
23426             case 18: // alt
23427                 break;
23428             default :
23429                 this.hidePopup();
23430                 
23431         }
23432     },
23433     
23434     
23435     onClick: function(e) 
23436     {
23437         e.stopPropagation();
23438         e.preventDefault();
23439         
23440         var target = e.getTarget();
23441         
23442         if(target.nodeName.toLowerCase() === 'i'){
23443             target = Roo.get(target).dom.parentNode;
23444         }
23445         
23446         var nodeName = target.nodeName;
23447         var className = target.className;
23448         var html = target.innerHTML;
23449         //Roo.log(nodeName);
23450         
23451         switch(nodeName.toLowerCase()) {
23452             case 'th':
23453                 switch(className) {
23454                     case 'switch':
23455                         this.showMode(1);
23456                         break;
23457                     case 'prev':
23458                     case 'next':
23459                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23460                         switch(this.viewMode){
23461                                 case 0:
23462                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23463                                         break;
23464                                 case 1:
23465                                 case 2:
23466                                         this.viewDate = this.moveYear(this.viewDate, dir);
23467                                         break;
23468                         }
23469                         this.fill();
23470                         break;
23471                     case 'today':
23472                         var date = new Date();
23473                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23474 //                        this.fill()
23475                         this.setValue(this.formatDate(this.date));
23476                         
23477                         this.hidePopup();
23478                         break;
23479                 }
23480                 break;
23481             case 'span':
23482                 if (className.indexOf('disabled') < 0) {
23483                 if (!this.viewDate) {
23484                     this.viewDate = new Date();
23485                 }
23486                 this.viewDate.setUTCDate(1);
23487                     if (className.indexOf('month') > -1) {
23488                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23489                     } else {
23490                         var year = parseInt(html, 10) || 0;
23491                         this.viewDate.setUTCFullYear(year);
23492                         
23493                     }
23494                     
23495                     if(this.singleMode){
23496                         this.setValue(this.formatDate(this.viewDate));
23497                         this.hidePopup();
23498                         return;
23499                     }
23500                     
23501                     this.showMode(-1);
23502                     this.fill();
23503                 }
23504                 break;
23505                 
23506             case 'td':
23507                 //Roo.log(className);
23508                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23509                     var day = parseInt(html, 10) || 1;
23510                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23511                         month = (this.viewDate || new Date()).getUTCMonth();
23512
23513                     if (className.indexOf('old') > -1) {
23514                         if(month === 0 ){
23515                             month = 11;
23516                             year -= 1;
23517                         }else{
23518                             month -= 1;
23519                         }
23520                     } else if (className.indexOf('new') > -1) {
23521                         if (month == 11) {
23522                             month = 0;
23523                             year += 1;
23524                         } else {
23525                             month += 1;
23526                         }
23527                     }
23528                     //Roo.log([year,month,day]);
23529                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23530                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23531 //                    this.fill();
23532                     //Roo.log(this.formatDate(this.date));
23533                     this.setValue(this.formatDate(this.date));
23534                     this.hidePopup();
23535                 }
23536                 break;
23537         }
23538     },
23539     
23540     setStartDate: function(startDate)
23541     {
23542         this.startDate = startDate || -Infinity;
23543         if (this.startDate !== -Infinity) {
23544             this.startDate = this.parseDate(this.startDate);
23545         }
23546         this.update();
23547         this.updateNavArrows();
23548     },
23549
23550     setEndDate: function(endDate)
23551     {
23552         this.endDate = endDate || Infinity;
23553         if (this.endDate !== Infinity) {
23554             this.endDate = this.parseDate(this.endDate);
23555         }
23556         this.update();
23557         this.updateNavArrows();
23558     },
23559     
23560     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23561     {
23562         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23563         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23564             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23565         }
23566         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23567             return parseInt(d, 10);
23568         });
23569         this.update();
23570         this.updateNavArrows();
23571     },
23572     
23573     updateNavArrows: function() 
23574     {
23575         if(this.singleMode){
23576             return;
23577         }
23578         
23579         var d = new Date(this.viewDate),
23580         year = d.getUTCFullYear(),
23581         month = d.getUTCMonth();
23582         
23583         Roo.each(this.picker().select('.prev', true).elements, function(v){
23584             v.show();
23585             switch (this.viewMode) {
23586                 case 0:
23587
23588                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23589                         v.hide();
23590                     }
23591                     break;
23592                 case 1:
23593                 case 2:
23594                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23595                         v.hide();
23596                     }
23597                     break;
23598             }
23599         });
23600         
23601         Roo.each(this.picker().select('.next', true).elements, function(v){
23602             v.show();
23603             switch (this.viewMode) {
23604                 case 0:
23605
23606                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23607                         v.hide();
23608                     }
23609                     break;
23610                 case 1:
23611                 case 2:
23612                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23613                         v.hide();
23614                     }
23615                     break;
23616             }
23617         })
23618     },
23619     
23620     moveMonth: function(date, dir)
23621     {
23622         if (!dir) {
23623             return date;
23624         }
23625         var new_date = new Date(date.valueOf()),
23626         day = new_date.getUTCDate(),
23627         month = new_date.getUTCMonth(),
23628         mag = Math.abs(dir),
23629         new_month, test;
23630         dir = dir > 0 ? 1 : -1;
23631         if (mag == 1){
23632             test = dir == -1
23633             // If going back one month, make sure month is not current month
23634             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23635             ? function(){
23636                 return new_date.getUTCMonth() == month;
23637             }
23638             // If going forward one month, make sure month is as expected
23639             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23640             : function(){
23641                 return new_date.getUTCMonth() != new_month;
23642             };
23643             new_month = month + dir;
23644             new_date.setUTCMonth(new_month);
23645             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23646             if (new_month < 0 || new_month > 11) {
23647                 new_month = (new_month + 12) % 12;
23648             }
23649         } else {
23650             // For magnitudes >1, move one month at a time...
23651             for (var i=0; i<mag; i++) {
23652                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23653                 new_date = this.moveMonth(new_date, dir);
23654             }
23655             // ...then reset the day, keeping it in the new month
23656             new_month = new_date.getUTCMonth();
23657             new_date.setUTCDate(day);
23658             test = function(){
23659                 return new_month != new_date.getUTCMonth();
23660             };
23661         }
23662         // Common date-resetting loop -- if date is beyond end of month, make it
23663         // end of month
23664         while (test()){
23665             new_date.setUTCDate(--day);
23666             new_date.setUTCMonth(new_month);
23667         }
23668         return new_date;
23669     },
23670
23671     moveYear: function(date, dir)
23672     {
23673         return this.moveMonth(date, dir*12);
23674     },
23675
23676     dateWithinRange: function(date)
23677     {
23678         return date >= this.startDate && date <= this.endDate;
23679     },
23680
23681     
23682     remove: function() 
23683     {
23684         this.picker().remove();
23685     },
23686     
23687     validateValue : function(value)
23688     {
23689         if(this.getVisibilityEl().hasClass('hidden')){
23690             return true;
23691         }
23692         
23693         if(value.length < 1)  {
23694             if(this.allowBlank){
23695                 return true;
23696             }
23697             return false;
23698         }
23699         
23700         if(value.length < this.minLength){
23701             return false;
23702         }
23703         if(value.length > this.maxLength){
23704             return false;
23705         }
23706         if(this.vtype){
23707             var vt = Roo.form.VTypes;
23708             if(!vt[this.vtype](value, this)){
23709                 return false;
23710             }
23711         }
23712         if(typeof this.validator == "function"){
23713             var msg = this.validator(value);
23714             if(msg !== true){
23715                 return false;
23716             }
23717         }
23718         
23719         if(this.regex && !this.regex.test(value)){
23720             return false;
23721         }
23722         
23723         if(typeof(this.parseDate(value)) == 'undefined'){
23724             return false;
23725         }
23726         
23727         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23728             return false;
23729         }      
23730         
23731         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23732             return false;
23733         } 
23734         
23735         
23736         return true;
23737     },
23738     
23739     reset : function()
23740     {
23741         this.date = this.viewDate = '';
23742         
23743         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23744     }
23745    
23746 });
23747
23748 Roo.apply(Roo.bootstrap.form.DateField,  {
23749     
23750     head : {
23751         tag: 'thead',
23752         cn: [
23753         {
23754             tag: 'tr',
23755             cn: [
23756             {
23757                 tag: 'th',
23758                 cls: 'prev',
23759                 html: '<i class="fa fa-arrow-left"/>'
23760             },
23761             {
23762                 tag: 'th',
23763                 cls: 'switch',
23764                 colspan: '5'
23765             },
23766             {
23767                 tag: 'th',
23768                 cls: 'next',
23769                 html: '<i class="fa fa-arrow-right"/>'
23770             }
23771
23772             ]
23773         }
23774         ]
23775     },
23776     
23777     content : {
23778         tag: 'tbody',
23779         cn: [
23780         {
23781             tag: 'tr',
23782             cn: [
23783             {
23784                 tag: 'td',
23785                 colspan: '7'
23786             }
23787             ]
23788         }
23789         ]
23790     },
23791     
23792     footer : {
23793         tag: 'tfoot',
23794         cn: [
23795         {
23796             tag: 'tr',
23797             cn: [
23798             {
23799                 tag: 'th',
23800                 colspan: '7',
23801                 cls: 'today'
23802             }
23803                     
23804             ]
23805         }
23806         ]
23807     },
23808     
23809     dates:{
23810         en: {
23811             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23812             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23813             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23814             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23815             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23816             today: "Today"
23817         }
23818     },
23819     
23820     modes: [
23821     {
23822         clsName: 'days',
23823         navFnc: 'Month',
23824         navStep: 1
23825     },
23826     {
23827         clsName: 'months',
23828         navFnc: 'FullYear',
23829         navStep: 1
23830     },
23831     {
23832         clsName: 'years',
23833         navFnc: 'FullYear',
23834         navStep: 10
23835     }]
23836 });
23837
23838 Roo.apply(Roo.bootstrap.form.DateField,  {
23839   
23840     template : {
23841         tag: 'div',
23842         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23843         cn: [
23844         {
23845             tag: 'div',
23846             cls: 'datepicker-days',
23847             cn: [
23848             {
23849                 tag: 'table',
23850                 cls: 'table-condensed',
23851                 cn:[
23852                 Roo.bootstrap.form.DateField.head,
23853                 {
23854                     tag: 'tbody'
23855                 },
23856                 Roo.bootstrap.form.DateField.footer
23857                 ]
23858             }
23859             ]
23860         },
23861         {
23862             tag: 'div',
23863             cls: 'datepicker-months',
23864             cn: [
23865             {
23866                 tag: 'table',
23867                 cls: 'table-condensed',
23868                 cn:[
23869                 Roo.bootstrap.form.DateField.head,
23870                 Roo.bootstrap.form.DateField.content,
23871                 Roo.bootstrap.form.DateField.footer
23872                 ]
23873             }
23874             ]
23875         },
23876         {
23877             tag: 'div',
23878             cls: 'datepicker-years',
23879             cn: [
23880             {
23881                 tag: 'table',
23882                 cls: 'table-condensed',
23883                 cn:[
23884                 Roo.bootstrap.form.DateField.head,
23885                 Roo.bootstrap.form.DateField.content,
23886                 Roo.bootstrap.form.DateField.footer
23887                 ]
23888             }
23889             ]
23890         }
23891         ]
23892     }
23893 });
23894
23895  
23896
23897  /*
23898  * - LGPL
23899  *
23900  * TimeField
23901  * 
23902  */
23903
23904 /**
23905  * @class Roo.bootstrap.form.TimeField
23906  * @extends Roo.bootstrap.form.Input
23907  * Bootstrap DateField class
23908  * 
23909  * 
23910  * @constructor
23911  * Create a new TimeField
23912  * @param {Object} config The config object
23913  */
23914
23915 Roo.bootstrap.form.TimeField = function(config){
23916     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23917     this.addEvents({
23918             /**
23919              * @event show
23920              * Fires when this field show.
23921              * @param {Roo.bootstrap.form.DateField} thisthis
23922              * @param {Mixed} date The date value
23923              */
23924             show : true,
23925             /**
23926              * @event show
23927              * Fires when this field hide.
23928              * @param {Roo.bootstrap.form.DateField} this
23929              * @param {Mixed} date The date value
23930              */
23931             hide : true,
23932             /**
23933              * @event select
23934              * Fires when select a date.
23935              * @param {Roo.bootstrap.form.DateField} this
23936              * @param {Mixed} date The date value
23937              */
23938             select : true
23939         });
23940 };
23941
23942 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23943     
23944     /**
23945      * @cfg {String} format
23946      * The default time format string which can be overriden for localization support.  The format must be
23947      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23948      */
23949     format : "H:i",
23950
23951     getAutoCreate : function()
23952     {
23953         this.after = '<i class="fa far fa-clock"></i>';
23954         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23955         
23956          
23957     },
23958     onRender: function(ct, position)
23959     {
23960         
23961         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23962                 
23963         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23964         
23965         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23966         
23967         this.pop = this.picker().select('>.datepicker-time',true).first();
23968         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23969         
23970         this.picker().on('mousedown', this.onMousedown, this);
23971         this.picker().on('click', this.onClick, this);
23972         
23973         this.picker().addClass('datepicker-dropdown');
23974     
23975         this.fillTime();
23976         this.update();
23977             
23978         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23979         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23980         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23981         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23982         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23983         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23984
23985     },
23986     
23987     fireKey: function(e){
23988         if (!this.picker().isVisible()){
23989             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23990                 this.show();
23991             }
23992             return;
23993         }
23994
23995         e.preventDefault();
23996         
23997         switch(e.keyCode){
23998             case 27: // escape
23999                 this.hide();
24000                 break;
24001             case 37: // left
24002             case 39: // right
24003                 this.onTogglePeriod();
24004                 break;
24005             case 38: // up
24006                 this.onIncrementMinutes();
24007                 break;
24008             case 40: // down
24009                 this.onDecrementMinutes();
24010                 break;
24011             case 13: // enter
24012             case 9: // tab
24013                 this.setTime();
24014                 break;
24015         }
24016     },
24017     
24018     onClick: function(e) {
24019         e.stopPropagation();
24020         e.preventDefault();
24021     },
24022     
24023     picker : function()
24024     {
24025         return this.pickerEl;
24026     },
24027     
24028     fillTime: function()
24029     {    
24030         var time = this.pop.select('tbody', true).first();
24031         
24032         time.dom.innerHTML = '';
24033         
24034         time.createChild({
24035             tag: 'tr',
24036             cn: [
24037                 {
24038                     tag: 'td',
24039                     cn: [
24040                         {
24041                             tag: 'a',
24042                             href: '#',
24043                             cls: 'btn',
24044                             cn: [
24045                                 {
24046                                     tag: 'i',
24047                                     cls: 'hours-up fa fas fa-chevron-up'
24048                                 }
24049                             ]
24050                         } 
24051                     ]
24052                 },
24053                 {
24054                     tag: 'td',
24055                     cls: 'separator'
24056                 },
24057                 {
24058                     tag: 'td',
24059                     cn: [
24060                         {
24061                             tag: 'a',
24062                             href: '#',
24063                             cls: 'btn',
24064                             cn: [
24065                                 {
24066                                     tag: 'i',
24067                                     cls: 'minutes-up fa fas fa-chevron-up'
24068                                 }
24069                             ]
24070                         }
24071                     ]
24072                 },
24073                 {
24074                     tag: 'td',
24075                     cls: 'separator'
24076                 }
24077             ]
24078         });
24079         
24080         time.createChild({
24081             tag: 'tr',
24082             cn: [
24083                 {
24084                     tag: 'td',
24085                     cn: [
24086                         {
24087                             tag: 'span',
24088                             cls: 'timepicker-hour',
24089                             html: '00'
24090                         }  
24091                     ]
24092                 },
24093                 {
24094                     tag: 'td',
24095                     cls: 'separator',
24096                     html: ':'
24097                 },
24098                 {
24099                     tag: 'td',
24100                     cn: [
24101                         {
24102                             tag: 'span',
24103                             cls: 'timepicker-minute',
24104                             html: '00'
24105                         }  
24106                     ]
24107                 },
24108                 {
24109                     tag: 'td',
24110                     cls: 'separator'
24111                 },
24112                 {
24113                     tag: 'td',
24114                     cn: [
24115                         {
24116                             tag: 'button',
24117                             type: 'button',
24118                             cls: 'btn btn-primary period',
24119                             html: 'AM'
24120                             
24121                         }
24122                     ]
24123                 }
24124             ]
24125         });
24126         
24127         time.createChild({
24128             tag: 'tr',
24129             cn: [
24130                 {
24131                     tag: 'td',
24132                     cn: [
24133                         {
24134                             tag: 'a',
24135                             href: '#',
24136                             cls: 'btn',
24137                             cn: [
24138                                 {
24139                                     tag: 'span',
24140                                     cls: 'hours-down fa fas fa-chevron-down'
24141                                 }
24142                             ]
24143                         }
24144                     ]
24145                 },
24146                 {
24147                     tag: 'td',
24148                     cls: 'separator'
24149                 },
24150                 {
24151                     tag: 'td',
24152                     cn: [
24153                         {
24154                             tag: 'a',
24155                             href: '#',
24156                             cls: 'btn',
24157                             cn: [
24158                                 {
24159                                     tag: 'span',
24160                                     cls: 'minutes-down fa fas fa-chevron-down'
24161                                 }
24162                             ]
24163                         }
24164                     ]
24165                 },
24166                 {
24167                     tag: 'td',
24168                     cls: 'separator'
24169                 }
24170             ]
24171         });
24172         
24173     },
24174     
24175     update: function()
24176     {
24177         
24178         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24179         
24180         this.fill();
24181     },
24182     
24183     fill: function() 
24184     {
24185         var hours = this.time.getHours();
24186         var minutes = this.time.getMinutes();
24187         var period = 'AM';
24188         
24189         if(hours > 11){
24190             period = 'PM';
24191         }
24192         
24193         if(hours == 0){
24194             hours = 12;
24195         }
24196         
24197         
24198         if(hours > 12){
24199             hours = hours - 12;
24200         }
24201         
24202         if(hours < 10){
24203             hours = '0' + hours;
24204         }
24205         
24206         if(minutes < 10){
24207             minutes = '0' + minutes;
24208         }
24209         
24210         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24211         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24212         this.pop.select('button', true).first().dom.innerHTML = period;
24213         
24214     },
24215     
24216     place: function()
24217     {   
24218         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24219         
24220         var cls = ['bottom'];
24221         
24222         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24223             cls.pop();
24224             cls.push('top');
24225         }
24226         
24227         cls.push('right');
24228         
24229         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24230             cls.pop();
24231             cls.push('left');
24232         }
24233         //this.picker().setXY(20000,20000);
24234         this.picker().addClass(cls.join('-'));
24235         
24236         var _this = this;
24237         
24238         Roo.each(cls, function(c){
24239             if(c == 'bottom'){
24240                 (function() {
24241                  //  
24242                 }).defer(200);
24243                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24244                 //_this.picker().setTop(_this.inputEl().getHeight());
24245                 return;
24246             }
24247             if(c == 'top'){
24248                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24249                 
24250                 //_this.picker().setTop(0 - _this.picker().getHeight());
24251                 return;
24252             }
24253             /*
24254             if(c == 'left'){
24255                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24256                 return;
24257             }
24258             if(c == 'right'){
24259                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24260                 return;
24261             }
24262             */
24263         });
24264         
24265     },
24266   
24267     onFocus : function()
24268     {
24269         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24270         this.show();
24271     },
24272     
24273     onBlur : function()
24274     {
24275         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24276         this.hide();
24277     },
24278     
24279     show : function()
24280     {
24281         this.picker().show();
24282         this.pop.show();
24283         this.update();
24284         this.place();
24285         
24286         this.fireEvent('show', this, this.date);
24287     },
24288     
24289     hide : function()
24290     {
24291         this.picker().hide();
24292         this.pop.hide();
24293         
24294         this.fireEvent('hide', this, this.date);
24295     },
24296     
24297     setTime : function()
24298     {
24299         this.hide();
24300         this.setValue(this.time.format(this.format));
24301         
24302         this.fireEvent('select', this, this.date);
24303         
24304         
24305     },
24306     
24307     onMousedown: function(e){
24308         e.stopPropagation();
24309         e.preventDefault();
24310     },
24311     
24312     onIncrementHours: function()
24313     {
24314         Roo.log('onIncrementHours');
24315         this.time = this.time.add(Date.HOUR, 1);
24316         this.update();
24317         
24318     },
24319     
24320     onDecrementHours: function()
24321     {
24322         Roo.log('onDecrementHours');
24323         this.time = this.time.add(Date.HOUR, -1);
24324         this.update();
24325     },
24326     
24327     onIncrementMinutes: function()
24328     {
24329         Roo.log('onIncrementMinutes');
24330         this.time = this.time.add(Date.MINUTE, 1);
24331         this.update();
24332     },
24333     
24334     onDecrementMinutes: function()
24335     {
24336         Roo.log('onDecrementMinutes');
24337         this.time = this.time.add(Date.MINUTE, -1);
24338         this.update();
24339     },
24340     
24341     onTogglePeriod: function()
24342     {
24343         Roo.log('onTogglePeriod');
24344         this.time = this.time.add(Date.HOUR, 12);
24345         this.update();
24346     }
24347     
24348    
24349 });
24350  
24351
24352 Roo.apply(Roo.bootstrap.form.TimeField,  {
24353   
24354     template : {
24355         tag: 'div',
24356         cls: 'datepicker dropdown-menu',
24357         cn: [
24358             {
24359                 tag: 'div',
24360                 cls: 'datepicker-time',
24361                 cn: [
24362                 {
24363                     tag: 'table',
24364                     cls: 'table-condensed',
24365                     cn:[
24366                         {
24367                             tag: 'tbody',
24368                             cn: [
24369                                 {
24370                                     tag: 'tr',
24371                                     cn: [
24372                                     {
24373                                         tag: 'td',
24374                                         colspan: '7'
24375                                     }
24376                                     ]
24377                                 }
24378                             ]
24379                         },
24380                         {
24381                             tag: 'tfoot',
24382                             cn: [
24383                                 {
24384                                     tag: 'tr',
24385                                     cn: [
24386                                     {
24387                                         tag: 'th',
24388                                         colspan: '7',
24389                                         cls: '',
24390                                         cn: [
24391                                             {
24392                                                 tag: 'button',
24393                                                 cls: 'btn btn-info ok',
24394                                                 html: 'OK'
24395                                             }
24396                                         ]
24397                                     }
24398                     
24399                                     ]
24400                                 }
24401                             ]
24402                         }
24403                     ]
24404                 }
24405                 ]
24406             }
24407         ]
24408     }
24409 });
24410
24411  
24412
24413  /*
24414  * - LGPL
24415  *
24416  * MonthField
24417  * 
24418  */
24419
24420 /**
24421  * @class Roo.bootstrap.form.MonthField
24422  * @extends Roo.bootstrap.form.Input
24423  * Bootstrap MonthField class
24424  * 
24425  * @cfg {String} language default en
24426  * 
24427  * @constructor
24428  * Create a new MonthField
24429  * @param {Object} config The config object
24430  */
24431
24432 Roo.bootstrap.form.MonthField = function(config){
24433     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24434     
24435     this.addEvents({
24436         /**
24437          * @event show
24438          * Fires when this field show.
24439          * @param {Roo.bootstrap.form.MonthField} this
24440          * @param {Mixed} date The date value
24441          */
24442         show : true,
24443         /**
24444          * @event show
24445          * Fires when this field hide.
24446          * @param {Roo.bootstrap.form.MonthField} this
24447          * @param {Mixed} date The date value
24448          */
24449         hide : true,
24450         /**
24451          * @event select
24452          * Fires when select a date.
24453          * @param {Roo.bootstrap.form.MonthField} this
24454          * @param {String} oldvalue The old value
24455          * @param {String} newvalue The new value
24456          */
24457         select : true
24458     });
24459 };
24460
24461 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24462     
24463     onRender: function(ct, position)
24464     {
24465         
24466         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24467         
24468         this.language = this.language || 'en';
24469         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24470         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24471         
24472         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24473         this.isInline = false;
24474         this.isInput = true;
24475         this.component = this.el.select('.add-on', true).first() || false;
24476         this.component = (this.component && this.component.length === 0) ? false : this.component;
24477         this.hasInput = this.component && this.inputEL().length;
24478         
24479         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24480         
24481         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24482         
24483         this.picker().on('mousedown', this.onMousedown, this);
24484         this.picker().on('click', this.onClick, this);
24485         
24486         this.picker().addClass('datepicker-dropdown');
24487         
24488         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24489             v.setStyle('width', '189px');
24490         });
24491         
24492         this.fillMonths();
24493         
24494         this.update();
24495         
24496         if(this.isInline) {
24497             this.show();
24498         }
24499         
24500     },
24501     
24502     setValue: function(v, suppressEvent)
24503     {   
24504         var o = this.getValue();
24505         
24506         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24507         
24508         this.update();
24509
24510         if(suppressEvent !== true){
24511             this.fireEvent('select', this, o, v);
24512         }
24513         
24514     },
24515     
24516     getValue: function()
24517     {
24518         return this.value;
24519     },
24520     
24521     onClick: function(e) 
24522     {
24523         e.stopPropagation();
24524         e.preventDefault();
24525         
24526         var target = e.getTarget();
24527         
24528         if(target.nodeName.toLowerCase() === 'i'){
24529             target = Roo.get(target).dom.parentNode;
24530         }
24531         
24532         var nodeName = target.nodeName;
24533         var className = target.className;
24534         var html = target.innerHTML;
24535         
24536         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24537             return;
24538         }
24539         
24540         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24541         
24542         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24543         
24544         this.hide();
24545                         
24546     },
24547     
24548     picker : function()
24549     {
24550         return this.pickerEl;
24551     },
24552     
24553     fillMonths: function()
24554     {    
24555         var i = 0;
24556         var months = this.picker().select('>.datepicker-months td', true).first();
24557         
24558         months.dom.innerHTML = '';
24559         
24560         while (i < 12) {
24561             var month = {
24562                 tag: 'span',
24563                 cls: 'month',
24564                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24565             };
24566             
24567             months.createChild(month);
24568         }
24569         
24570     },
24571     
24572     update: function()
24573     {
24574         var _this = this;
24575         
24576         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24577             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24578         }
24579         
24580         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24581             e.removeClass('active');
24582             
24583             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24584                 e.addClass('active');
24585             }
24586         })
24587     },
24588     
24589     place: function()
24590     {
24591         if(this.isInline) {
24592             return;
24593         }
24594         
24595         this.picker().removeClass(['bottom', 'top']);
24596         
24597         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24598             /*
24599              * place to the top of element!
24600              *
24601              */
24602             
24603             this.picker().addClass('top');
24604             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24605             
24606             return;
24607         }
24608         
24609         this.picker().addClass('bottom');
24610         
24611         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24612     },
24613     
24614     onFocus : function()
24615     {
24616         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24617         this.show();
24618     },
24619     
24620     onBlur : function()
24621     {
24622         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24623         
24624         var d = this.inputEl().getValue();
24625         
24626         this.setValue(d);
24627                 
24628         this.hide();
24629     },
24630     
24631     show : function()
24632     {
24633         this.picker().show();
24634         this.picker().select('>.datepicker-months', true).first().show();
24635         this.update();
24636         this.place();
24637         
24638         this.fireEvent('show', this, this.date);
24639     },
24640     
24641     hide : function()
24642     {
24643         if(this.isInline) {
24644             return;
24645         }
24646         this.picker().hide();
24647         this.fireEvent('hide', this, this.date);
24648         
24649     },
24650     
24651     onMousedown: function(e)
24652     {
24653         e.stopPropagation();
24654         e.preventDefault();
24655     },
24656     
24657     keyup: function(e)
24658     {
24659         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24660         this.update();
24661     },
24662
24663     fireKey: function(e)
24664     {
24665         if (!this.picker().isVisible()){
24666             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24667                 this.show();
24668             }
24669             return;
24670         }
24671         
24672         var dir;
24673         
24674         switch(e.keyCode){
24675             case 27: // escape
24676                 this.hide();
24677                 e.preventDefault();
24678                 break;
24679             case 37: // left
24680             case 39: // right
24681                 dir = e.keyCode == 37 ? -1 : 1;
24682                 
24683                 this.vIndex = this.vIndex + dir;
24684                 
24685                 if(this.vIndex < 0){
24686                     this.vIndex = 0;
24687                 }
24688                 
24689                 if(this.vIndex > 11){
24690                     this.vIndex = 11;
24691                 }
24692                 
24693                 if(isNaN(this.vIndex)){
24694                     this.vIndex = 0;
24695                 }
24696                 
24697                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24698                 
24699                 break;
24700             case 38: // up
24701             case 40: // down
24702                 
24703                 dir = e.keyCode == 38 ? -1 : 1;
24704                 
24705                 this.vIndex = this.vIndex + dir * 4;
24706                 
24707                 if(this.vIndex < 0){
24708                     this.vIndex = 0;
24709                 }
24710                 
24711                 if(this.vIndex > 11){
24712                     this.vIndex = 11;
24713                 }
24714                 
24715                 if(isNaN(this.vIndex)){
24716                     this.vIndex = 0;
24717                 }
24718                 
24719                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24720                 break;
24721                 
24722             case 13: // enter
24723                 
24724                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24725                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24726                 }
24727                 
24728                 this.hide();
24729                 e.preventDefault();
24730                 break;
24731             case 9: // tab
24732                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24733                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24734                 }
24735                 this.hide();
24736                 break;
24737             case 16: // shift
24738             case 17: // ctrl
24739             case 18: // alt
24740                 break;
24741             default :
24742                 this.hide();
24743                 
24744         }
24745     },
24746     
24747     remove: function() 
24748     {
24749         this.picker().remove();
24750     }
24751    
24752 });
24753
24754 Roo.apply(Roo.bootstrap.form.MonthField,  {
24755     
24756     content : {
24757         tag: 'tbody',
24758         cn: [
24759         {
24760             tag: 'tr',
24761             cn: [
24762             {
24763                 tag: 'td',
24764                 colspan: '7'
24765             }
24766             ]
24767         }
24768         ]
24769     },
24770     
24771     dates:{
24772         en: {
24773             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24774             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24775         }
24776     }
24777 });
24778
24779 Roo.apply(Roo.bootstrap.form.MonthField,  {
24780   
24781     template : {
24782         tag: 'div',
24783         cls: 'datepicker dropdown-menu roo-dynamic',
24784         cn: [
24785             {
24786                 tag: 'div',
24787                 cls: 'datepicker-months',
24788                 cn: [
24789                 {
24790                     tag: 'table',
24791                     cls: 'table-condensed',
24792                     cn:[
24793                         Roo.bootstrap.form.DateField.content
24794                     ]
24795                 }
24796                 ]
24797             }
24798         ]
24799     }
24800 });
24801
24802  
24803
24804  
24805  /*
24806  * - LGPL
24807  *
24808  * CheckBox
24809  * 
24810  */
24811
24812 /**
24813  * @class Roo.bootstrap.form.CheckBox
24814  * @extends Roo.bootstrap.form.Input
24815  * Bootstrap CheckBox class
24816  * 
24817  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24818  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24819  * @cfg {String} boxLabel The text that appears beside the checkbox
24820  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24821  * @cfg {Boolean} checked initnal the element
24822  * @cfg {Boolean} inline inline the element (default false)
24823  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24824  * @cfg {String} tooltip label tooltip
24825  * 
24826  * @constructor
24827  * Create a new CheckBox
24828  * @param {Object} config The config object
24829  */
24830
24831 Roo.bootstrap.form.CheckBox = function(config){
24832     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24833    
24834     this.addEvents({
24835         /**
24836         * @event check
24837         * Fires when the element is checked or unchecked.
24838         * @param {Roo.bootstrap.form.CheckBox} this This input
24839         * @param {Boolean} checked The new checked value
24840         */
24841        check : true,
24842        /**
24843         * @event click
24844         * Fires when the element is click.
24845         * @param {Roo.bootstrap.form.CheckBox} this This input
24846         */
24847        click : true
24848     });
24849     
24850 };
24851
24852 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24853   
24854     inputType: 'checkbox',
24855     inputValue: 1,
24856     valueOff: 0,
24857     boxLabel: false,
24858     checked: false,
24859     weight : false,
24860     inline: false,
24861     tooltip : '',
24862     
24863     // checkbox success does not make any sense really.. 
24864     invalidClass : "",
24865     validClass : "",
24866     
24867     
24868     getAutoCreate : function()
24869     {
24870         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24871         
24872         var id = Roo.id();
24873         
24874         var cfg = {};
24875         
24876         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24877         
24878         if(this.inline){
24879             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24880         }
24881         
24882         var input =  {
24883             tag: 'input',
24884             id : id,
24885             type : this.inputType,
24886             value : this.inputValue,
24887             cls : 'roo-' + this.inputType, //'form-box',
24888             placeholder : this.placeholder || ''
24889             
24890         };
24891         
24892         if(this.inputType != 'radio'){
24893             var hidden =  {
24894                 tag: 'input',
24895                 type : 'hidden',
24896                 cls : 'roo-hidden-value',
24897                 value : this.checked ? this.inputValue : this.valueOff
24898             };
24899         }
24900         
24901             
24902         if (this.weight) { // Validity check?
24903             cfg.cls += " " + this.inputType + "-" + this.weight;
24904         }
24905         
24906         if (this.disabled) {
24907             input.disabled=true;
24908         }
24909         
24910         if(this.checked){
24911             input.checked = this.checked;
24912         }
24913         
24914         if (this.name) {
24915             
24916             input.name = this.name;
24917             
24918             if(this.inputType != 'radio'){
24919                 hidden.name = this.name;
24920                 input.name = '_hidden_' + this.name;
24921             }
24922         }
24923         
24924         if (this.size) {
24925             input.cls += ' input-' + this.size;
24926         }
24927         
24928         var settings=this;
24929         
24930         ['xs','sm','md','lg'].map(function(size){
24931             if (settings[size]) {
24932                 cfg.cls += ' col-' + size + '-' + settings[size];
24933             }
24934         });
24935         
24936         var inputblock = input;
24937          
24938         if (this.before || this.after) {
24939             
24940             inputblock = {
24941                 cls : 'input-group',
24942                 cn :  [] 
24943             };
24944             
24945             if (this.before) {
24946                 inputblock.cn.push({
24947                     tag :'span',
24948                     cls : 'input-group-addon',
24949                     html : this.before
24950                 });
24951             }
24952             
24953             inputblock.cn.push(input);
24954             
24955             if(this.inputType != 'radio'){
24956                 inputblock.cn.push(hidden);
24957             }
24958             
24959             if (this.after) {
24960                 inputblock.cn.push({
24961                     tag :'span',
24962                     cls : 'input-group-addon',
24963                     html : this.after
24964                 });
24965             }
24966             
24967         }
24968         var boxLabelCfg = false;
24969         
24970         if(this.boxLabel){
24971            
24972             boxLabelCfg = {
24973                 tag: 'label',
24974                 //'for': id, // box label is handled by onclick - so no for...
24975                 cls: 'box-label',
24976                 html: this.boxLabel
24977             };
24978             if(this.tooltip){
24979                 boxLabelCfg.tooltip = this.tooltip;
24980             }
24981              
24982         }
24983         
24984         
24985         if (align ==='left' && this.fieldLabel.length) {
24986 //                Roo.log("left and has label");
24987             cfg.cn = [
24988                 {
24989                     tag: 'label',
24990                     'for' :  id,
24991                     cls : 'control-label',
24992                     html : this.fieldLabel
24993                 },
24994                 {
24995                     cls : "", 
24996                     cn: [
24997                         inputblock
24998                     ]
24999                 }
25000             ];
25001             
25002             if (boxLabelCfg) {
25003                 cfg.cn[1].cn.push(boxLabelCfg);
25004             }
25005             
25006             if(this.labelWidth > 12){
25007                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25008             }
25009             
25010             if(this.labelWidth < 13 && this.labelmd == 0){
25011                 this.labelmd = this.labelWidth;
25012             }
25013             
25014             if(this.labellg > 0){
25015                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25016                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25017             }
25018             
25019             if(this.labelmd > 0){
25020                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25021                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25022             }
25023             
25024             if(this.labelsm > 0){
25025                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25026                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25027             }
25028             
25029             if(this.labelxs > 0){
25030                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25031                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25032             }
25033             
25034         } else if ( this.fieldLabel.length) {
25035 //                Roo.log(" label");
25036                 cfg.cn = [
25037                    
25038                     {
25039                         tag: this.boxLabel ? 'span' : 'label',
25040                         'for': id,
25041                         cls: 'control-label box-input-label',
25042                         //cls : 'input-group-addon',
25043                         html : this.fieldLabel
25044                     },
25045                     
25046                     inputblock
25047                     
25048                 ];
25049                 if (boxLabelCfg) {
25050                     cfg.cn.push(boxLabelCfg);
25051                 }
25052
25053         } else {
25054             
25055 //                Roo.log(" no label && no align");
25056                 cfg.cn = [  inputblock ] ;
25057                 if (boxLabelCfg) {
25058                     cfg.cn.push(boxLabelCfg);
25059                 }
25060
25061                 
25062         }
25063         
25064        
25065         
25066         if(this.inputType != 'radio'){
25067             cfg.cn.push(hidden);
25068         }
25069         
25070         return cfg;
25071         
25072     },
25073     
25074     /**
25075      * return the real input element.
25076      */
25077     inputEl: function ()
25078     {
25079         return this.el.select('input.roo-' + this.inputType,true).first();
25080     },
25081     hiddenEl: function ()
25082     {
25083         return this.el.select('input.roo-hidden-value',true).first();
25084     },
25085     
25086     labelEl: function()
25087     {
25088         return this.el.select('label.control-label',true).first();
25089     },
25090     /* depricated... */
25091     
25092     label: function()
25093     {
25094         return this.labelEl();
25095     },
25096     
25097     boxLabelEl: function()
25098     {
25099         return this.el.select('label.box-label',true).first();
25100     },
25101     
25102     initEvents : function()
25103     {
25104 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25105         
25106         this.inputEl().on('click', this.onClick,  this);
25107         
25108         if (this.boxLabel) { 
25109             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25110         }
25111         
25112         this.startValue = this.getValue();
25113         
25114         if(this.groupId){
25115             Roo.bootstrap.form.CheckBox.register(this);
25116         }
25117     },
25118     
25119     onClick : function(e)
25120     {   
25121         if(this.fireEvent('click', this, e) !== false){
25122             this.setChecked(!this.checked);
25123         }
25124         
25125     },
25126     
25127     setChecked : function(state,suppressEvent)
25128     {
25129         this.startValue = this.getValue();
25130
25131         if(this.inputType == 'radio'){
25132             
25133             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25134                 e.dom.checked = false;
25135             });
25136             
25137             this.inputEl().dom.checked = true;
25138             
25139             this.inputEl().dom.value = this.inputValue;
25140             
25141             if(suppressEvent !== true){
25142                 this.fireEvent('check', this, true);
25143             }
25144             
25145             this.validate();
25146             
25147             return;
25148         }
25149         
25150         this.checked = state;
25151         
25152         this.inputEl().dom.checked = state;
25153         
25154         
25155         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25156         
25157         if(suppressEvent !== true){
25158             this.fireEvent('check', this, state);
25159         }
25160         
25161         this.validate();
25162     },
25163     
25164     getValue : function()
25165     {
25166         if(this.inputType == 'radio'){
25167             return this.getGroupValue();
25168         }
25169         
25170         return this.hiddenEl().dom.value;
25171         
25172     },
25173     
25174     getGroupValue : function()
25175     {
25176         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25177             return '';
25178         }
25179         
25180         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25181     },
25182     
25183     setValue : function(v,suppressEvent)
25184     {
25185         if(this.inputType == 'radio'){
25186             this.setGroupValue(v, suppressEvent);
25187             return;
25188         }
25189         
25190         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25191         
25192         this.validate();
25193     },
25194     
25195     setGroupValue : function(v, suppressEvent)
25196     {
25197         this.startValue = this.getValue();
25198         
25199         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25200             e.dom.checked = false;
25201             
25202             if(e.dom.value == v){
25203                 e.dom.checked = true;
25204             }
25205         });
25206         
25207         if(suppressEvent !== true){
25208             this.fireEvent('check', this, true);
25209         }
25210
25211         this.validate();
25212         
25213         return;
25214     },
25215     
25216     validate : function()
25217     {
25218         if(this.getVisibilityEl().hasClass('hidden')){
25219             return true;
25220         }
25221         
25222         if(
25223                 this.disabled || 
25224                 (this.inputType == 'radio' && this.validateRadio()) ||
25225                 (this.inputType == 'checkbox' && this.validateCheckbox())
25226         ){
25227             this.markValid();
25228             return true;
25229         }
25230         
25231         this.markInvalid();
25232         return false;
25233     },
25234     
25235     validateRadio : function()
25236     {
25237         if(this.getVisibilityEl().hasClass('hidden')){
25238             return true;
25239         }
25240         
25241         if(this.allowBlank){
25242             return true;
25243         }
25244         
25245         var valid = false;
25246         
25247         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25248             if(!e.dom.checked){
25249                 return;
25250             }
25251             
25252             valid = true;
25253             
25254             return false;
25255         });
25256         
25257         return valid;
25258     },
25259     
25260     validateCheckbox : function()
25261     {
25262         if(!this.groupId){
25263             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25264             //return (this.getValue() == this.inputValue) ? true : false;
25265         }
25266         
25267         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25268         
25269         if(!group){
25270             return false;
25271         }
25272         
25273         var r = false;
25274         
25275         for(var i in group){
25276             if(group[i].el.isVisible(true)){
25277                 r = false;
25278                 break;
25279             }
25280             
25281             r = true;
25282         }
25283         
25284         for(var i in group){
25285             if(r){
25286                 break;
25287             }
25288             
25289             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25290         }
25291         
25292         return r;
25293     },
25294     
25295     /**
25296      * Mark this field as valid
25297      */
25298     markValid : function()
25299     {
25300         var _this = this;
25301         
25302         this.fireEvent('valid', this);
25303         
25304         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25305         
25306         if(this.groupId){
25307             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25308         }
25309         
25310         if(label){
25311             label.markValid();
25312         }
25313
25314         if(this.inputType == 'radio'){
25315             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25316                 var fg = e.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             });
25325             
25326             return;
25327         }
25328
25329         if(!this.groupId){
25330             var fg = this.el.findParent('.form-group', false, true);
25331             if (Roo.bootstrap.version == 3) {
25332                 fg.removeClass([this.invalidClass, this.validClass]);
25333                 fg.addClass(this.validClass);
25334             } else {
25335                 fg.removeClass(['is-valid', 'is-invalid']);
25336                 fg.addClass('is-valid');
25337             }
25338             return;
25339         }
25340         
25341         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25342         
25343         if(!group){
25344             return;
25345         }
25346         
25347         for(var i in group){
25348             var fg = group[i].el.findParent('.form-group', false, true);
25349             if (Roo.bootstrap.version == 3) {
25350                 fg.removeClass([this.invalidClass, this.validClass]);
25351                 fg.addClass(this.validClass);
25352             } else {
25353                 fg.removeClass(['is-valid', 'is-invalid']);
25354                 fg.addClass('is-valid');
25355             }
25356         }
25357     },
25358     
25359      /**
25360      * Mark this field as invalid
25361      * @param {String} msg The validation message
25362      */
25363     markInvalid : function(msg)
25364     {
25365         if(this.allowBlank){
25366             return;
25367         }
25368         
25369         var _this = this;
25370         
25371         this.fireEvent('invalid', this, msg);
25372         
25373         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25374         
25375         if(this.groupId){
25376             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25377         }
25378         
25379         if(label){
25380             label.markInvalid();
25381         }
25382             
25383         if(this.inputType == 'radio'){
25384             
25385             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25386                 var fg = e.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             });
25395             
25396             return;
25397         }
25398         
25399         if(!this.groupId){
25400             var fg = this.el.findParent('.form-group', false, true);
25401             if (Roo.bootstrap.version == 3) {
25402                 fg.removeClass([_this.invalidClass, _this.validClass]);
25403                 fg.addClass(_this.invalidClass);
25404             } else {
25405                 fg.removeClass(['is-invalid', 'is-valid']);
25406                 fg.addClass('is-invalid');
25407             }
25408             return;
25409         }
25410         
25411         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25412         
25413         if(!group){
25414             return;
25415         }
25416         
25417         for(var i in group){
25418             var fg = group[i].el.findParent('.form-group', false, true);
25419             if (Roo.bootstrap.version == 3) {
25420                 fg.removeClass([_this.invalidClass, _this.validClass]);
25421                 fg.addClass(_this.invalidClass);
25422             } else {
25423                 fg.removeClass(['is-invalid', 'is-valid']);
25424                 fg.addClass('is-invalid');
25425             }
25426         }
25427         
25428     },
25429     
25430     clearInvalid : function()
25431     {
25432         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25433         
25434         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25435         
25436         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25437         
25438         if (label && label.iconEl) {
25439             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25440             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25441         }
25442     },
25443     
25444     disable : function()
25445     {
25446         if(this.inputType != 'radio'){
25447             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25448             return;
25449         }
25450         
25451         var _this = this;
25452         
25453         if(this.rendered){
25454             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25455                 _this.getActionEl().addClass(this.disabledClass);
25456                 e.dom.disabled = true;
25457             });
25458         }
25459         
25460         this.disabled = true;
25461         this.fireEvent("disable", this);
25462         return this;
25463     },
25464
25465     enable : function()
25466     {
25467         if(this.inputType != 'radio'){
25468             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25469             return;
25470         }
25471         
25472         var _this = this;
25473         
25474         if(this.rendered){
25475             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25476                 _this.getActionEl().removeClass(this.disabledClass);
25477                 e.dom.disabled = false;
25478             });
25479         }
25480         
25481         this.disabled = false;
25482         this.fireEvent("enable", this);
25483         return this;
25484     },
25485     
25486     setBoxLabel : function(v)
25487     {
25488         this.boxLabel = v;
25489         
25490         if(this.rendered){
25491             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25492         }
25493     }
25494
25495 });
25496
25497 Roo.apply(Roo.bootstrap.form.CheckBox, {
25498     
25499     groups: {},
25500     
25501      /**
25502     * register a CheckBox Group
25503     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25504     */
25505     register : function(checkbox)
25506     {
25507         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25508             this.groups[checkbox.groupId] = {};
25509         }
25510         
25511         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25512             return;
25513         }
25514         
25515         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25516         
25517     },
25518     /**
25519     * fetch a CheckBox Group based on the group ID
25520     * @param {string} the group ID
25521     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25522     */
25523     get: function(groupId) {
25524         if (typeof(this.groups[groupId]) == 'undefined') {
25525             return false;
25526         }
25527         
25528         return this.groups[groupId] ;
25529     }
25530     
25531     
25532 });
25533 /*
25534  * - LGPL
25535  *
25536  * RadioItem
25537  * 
25538  */
25539
25540 /**
25541  * @class Roo.bootstrap.form.Radio
25542  * @extends Roo.bootstrap.Component
25543  * Bootstrap Radio class
25544  * @cfg {String} boxLabel - the label associated
25545  * @cfg {String} value - the value of radio
25546  * 
25547  * @constructor
25548  * Create a new Radio
25549  * @param {Object} config The config object
25550  */
25551 Roo.bootstrap.form.Radio = function(config){
25552     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25553     
25554 };
25555
25556 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25557     
25558     boxLabel : '',
25559     
25560     value : '',
25561     
25562     getAutoCreate : function()
25563     {
25564         var cfg = {
25565             tag : 'div',
25566             cls : 'form-group radio',
25567             cn : [
25568                 {
25569                     tag : 'label',
25570                     cls : 'box-label',
25571                     html : this.boxLabel
25572                 }
25573             ]
25574         };
25575         
25576         return cfg;
25577     },
25578     
25579     initEvents : function() 
25580     {
25581         this.parent().register(this);
25582         
25583         this.el.on('click', this.onClick, this);
25584         
25585     },
25586     
25587     onClick : function(e)
25588     {
25589         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25590             this.setChecked(true);
25591         }
25592     },
25593     
25594     setChecked : function(state, suppressEvent)
25595     {
25596         this.parent().setValue(this.value, suppressEvent);
25597         
25598     },
25599     
25600     setBoxLabel : function(v)
25601     {
25602         this.boxLabel = v;
25603         
25604         if(this.rendered){
25605             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25606         }
25607     }
25608     
25609 });
25610  
25611
25612  /*
25613  * - LGPL
25614  *
25615  * Input
25616  * 
25617  */
25618
25619 /**
25620  * @class Roo.bootstrap.form.SecurePass
25621  * @extends Roo.bootstrap.form.Input
25622  * Bootstrap SecurePass class
25623  *
25624  * 
25625  * @constructor
25626  * Create a new SecurePass
25627  * @param {Object} config The config object
25628  */
25629  
25630 Roo.bootstrap.form.SecurePass = function (config) {
25631     // these go here, so the translation tool can replace them..
25632     this.errors = {
25633         PwdEmpty: "Please type a password, and then retype it to confirm.",
25634         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25635         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25636         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25637         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25638         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25639         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25640         TooWeak: "Your password is Too Weak."
25641     },
25642     this.meterLabel = "Password strength:";
25643     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25644     this.meterClass = [
25645         "roo-password-meter-tooweak", 
25646         "roo-password-meter-weak", 
25647         "roo-password-meter-medium", 
25648         "roo-password-meter-strong", 
25649         "roo-password-meter-grey"
25650     ];
25651     
25652     this.errors = {};
25653     
25654     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25655 }
25656
25657 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25658     /**
25659      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25660      * {
25661      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25662      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25663      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25664      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25665      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25666      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25667      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25668      * })
25669      */
25670     // private
25671     
25672     meterWidth: 300,
25673     errorMsg :'',    
25674     errors: false,
25675     imageRoot: '/',
25676     /**
25677      * @cfg {String/Object} Label for the strength meter (defaults to
25678      * 'Password strength:')
25679      */
25680     // private
25681     meterLabel: '',
25682     /**
25683      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25684      * ['Weak', 'Medium', 'Strong'])
25685      */
25686     // private    
25687     pwdStrengths: false,    
25688     // private
25689     strength: 0,
25690     // private
25691     _lastPwd: null,
25692     // private
25693     kCapitalLetter: 0,
25694     kSmallLetter: 1,
25695     kDigit: 2,
25696     kPunctuation: 3,
25697     
25698     insecure: false,
25699     // private
25700     initEvents: function ()
25701     {
25702         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25703
25704         if (this.el.is('input[type=password]') && Roo.isSafari) {
25705             this.el.on('keydown', this.SafariOnKeyDown, this);
25706         }
25707
25708         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25709     },
25710     // private
25711     onRender: function (ct, position)
25712     {
25713         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25714         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25715         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25716
25717         this.trigger.createChild({
25718                    cn: [
25719                     {
25720                     //id: 'PwdMeter',
25721                     tag: 'div',
25722                     cls: 'roo-password-meter-grey col-xs-12',
25723                     style: {
25724                         //width: 0,
25725                         //width: this.meterWidth + 'px'                                                
25726                         }
25727                     },
25728                     {                            
25729                          cls: 'roo-password-meter-text'                          
25730                     }
25731                 ]            
25732         });
25733
25734          
25735         if (this.hideTrigger) {
25736             this.trigger.setDisplayed(false);
25737         }
25738         this.setSize(this.width || '', this.height || '');
25739     },
25740     // private
25741     onDestroy: function ()
25742     {
25743         if (this.trigger) {
25744             this.trigger.removeAllListeners();
25745             this.trigger.remove();
25746         }
25747         if (this.wrap) {
25748             this.wrap.remove();
25749         }
25750         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25751     },
25752     // private
25753     checkStrength: function ()
25754     {
25755         var pwd = this.inputEl().getValue();
25756         if (pwd == this._lastPwd) {
25757             return;
25758         }
25759
25760         var strength;
25761         if (this.ClientSideStrongPassword(pwd)) {
25762             strength = 3;
25763         } else if (this.ClientSideMediumPassword(pwd)) {
25764             strength = 2;
25765         } else if (this.ClientSideWeakPassword(pwd)) {
25766             strength = 1;
25767         } else {
25768             strength = 0;
25769         }
25770         
25771         Roo.log('strength1: ' + strength);
25772         
25773         //var pm = this.trigger.child('div/div/div').dom;
25774         var pm = this.trigger.child('div/div');
25775         pm.removeClass(this.meterClass);
25776         pm.addClass(this.meterClass[strength]);
25777                 
25778         
25779         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25780                 
25781         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25782         
25783         this._lastPwd = pwd;
25784     },
25785     reset: function ()
25786     {
25787         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25788         
25789         this._lastPwd = '';
25790         
25791         var pm = this.trigger.child('div/div');
25792         pm.removeClass(this.meterClass);
25793         pm.addClass('roo-password-meter-grey');        
25794         
25795         
25796         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25797         
25798         pt.innerHTML = '';
25799         this.inputEl().dom.type='password';
25800     },
25801     // private
25802     validateValue: function (value)
25803     {
25804         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25805             return false;
25806         }
25807         if (value.length == 0) {
25808             if (this.allowBlank) {
25809                 this.clearInvalid();
25810                 return true;
25811             }
25812
25813             this.markInvalid(this.errors.PwdEmpty);
25814             this.errorMsg = this.errors.PwdEmpty;
25815             return false;
25816         }
25817         
25818         if(this.insecure){
25819             return true;
25820         }
25821         
25822         if (!value.match(/[\x21-\x7e]+/)) {
25823             this.markInvalid(this.errors.PwdBadChar);
25824             this.errorMsg = this.errors.PwdBadChar;
25825             return false;
25826         }
25827         if (value.length < 6) {
25828             this.markInvalid(this.errors.PwdShort);
25829             this.errorMsg = this.errors.PwdShort;
25830             return false;
25831         }
25832         if (value.length > 16) {
25833             this.markInvalid(this.errors.PwdLong);
25834             this.errorMsg = this.errors.PwdLong;
25835             return false;
25836         }
25837         var strength;
25838         if (this.ClientSideStrongPassword(value)) {
25839             strength = 3;
25840         } else if (this.ClientSideMediumPassword(value)) {
25841             strength = 2;
25842         } else if (this.ClientSideWeakPassword(value)) {
25843             strength = 1;
25844         } else {
25845             strength = 0;
25846         }
25847
25848         
25849         if (strength < 2) {
25850             //this.markInvalid(this.errors.TooWeak);
25851             this.errorMsg = this.errors.TooWeak;
25852             //return false;
25853         }
25854         
25855         
25856         console.log('strength2: ' + strength);
25857         
25858         //var pm = this.trigger.child('div/div/div').dom;
25859         
25860         var pm = this.trigger.child('div/div');
25861         pm.removeClass(this.meterClass);
25862         pm.addClass(this.meterClass[strength]);
25863                 
25864         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25865                 
25866         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25867         
25868         this.errorMsg = ''; 
25869         return true;
25870     },
25871     // private
25872     CharacterSetChecks: function (type)
25873     {
25874         this.type = type;
25875         this.fResult = false;
25876     },
25877     // private
25878     isctype: function (character, type)
25879     {
25880         switch (type) {  
25881             case this.kCapitalLetter:
25882                 if (character >= 'A' && character <= 'Z') {
25883                     return true;
25884                 }
25885                 break;
25886             
25887             case this.kSmallLetter:
25888                 if (character >= 'a' && character <= 'z') {
25889                     return true;
25890                 }
25891                 break;
25892             
25893             case this.kDigit:
25894                 if (character >= '0' && character <= '9') {
25895                     return true;
25896                 }
25897                 break;
25898             
25899             case this.kPunctuation:
25900                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25901                     return true;
25902                 }
25903                 break;
25904             
25905             default:
25906                 return false;
25907         }
25908
25909     },
25910     // private
25911     IsLongEnough: function (pwd, size)
25912     {
25913         return !(pwd == null || isNaN(size) || pwd.length < size);
25914     },
25915     // private
25916     SpansEnoughCharacterSets: function (word, nb)
25917     {
25918         if (!this.IsLongEnough(word, nb))
25919         {
25920             return false;
25921         }
25922
25923         var characterSetChecks = new Array(
25924             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25925             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25926         );
25927         
25928         for (var index = 0; index < word.length; ++index) {
25929             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25930                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25931                     characterSetChecks[nCharSet].fResult = true;
25932                     break;
25933                 }
25934             }
25935         }
25936
25937         var nCharSets = 0;
25938         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25939             if (characterSetChecks[nCharSet].fResult) {
25940                 ++nCharSets;
25941             }
25942         }
25943
25944         if (nCharSets < nb) {
25945             return false;
25946         }
25947         return true;
25948     },
25949     // private
25950     ClientSideStrongPassword: function (pwd)
25951     {
25952         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25953     },
25954     // private
25955     ClientSideMediumPassword: function (pwd)
25956     {
25957         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25958     },
25959     // private
25960     ClientSideWeakPassword: function (pwd)
25961     {
25962         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25963     }
25964           
25965 })//<script type="text/javascript">
25966
25967 /*
25968  * Based  Ext JS Library 1.1.1
25969  * Copyright(c) 2006-2007, Ext JS, LLC.
25970  * LGPL
25971  *
25972  */
25973  
25974 /**
25975  * @class Roo.HtmlEditorCore
25976  * @extends Roo.Component
25977  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25978  *
25979  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25980  */
25981
25982 Roo.HtmlEditorCore = function(config){
25983     
25984     
25985     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25986     
25987     
25988     this.addEvents({
25989         /**
25990          * @event initialize
25991          * Fires when the editor is fully initialized (including the iframe)
25992          * @param {Roo.HtmlEditorCore} this
25993          */
25994         initialize: true,
25995         /**
25996          * @event activate
25997          * Fires when the editor is first receives the focus. Any insertion must wait
25998          * until after this event.
25999          * @param {Roo.HtmlEditorCore} this
26000          */
26001         activate: true,
26002          /**
26003          * @event beforesync
26004          * Fires before the textarea is updated with content from the editor iframe. Return false
26005          * to cancel the sync.
26006          * @param {Roo.HtmlEditorCore} this
26007          * @param {String} html
26008          */
26009         beforesync: true,
26010          /**
26011          * @event beforepush
26012          * Fires before the iframe editor is updated with content from the textarea. Return false
26013          * to cancel the push.
26014          * @param {Roo.HtmlEditorCore} this
26015          * @param {String} html
26016          */
26017         beforepush: true,
26018          /**
26019          * @event sync
26020          * Fires when the textarea is updated with content from the editor iframe.
26021          * @param {Roo.HtmlEditorCore} this
26022          * @param {String} html
26023          */
26024         sync: true,
26025          /**
26026          * @event push
26027          * Fires when the iframe editor is updated with content from the textarea.
26028          * @param {Roo.HtmlEditorCore} this
26029          * @param {String} html
26030          */
26031         push: true,
26032         
26033         /**
26034          * @event editorevent
26035          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26036          * @param {Roo.HtmlEditorCore} this
26037          */
26038         editorevent: true
26039         
26040     });
26041     
26042     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26043     
26044     // defaults : white / black...
26045     this.applyBlacklists();
26046     
26047     
26048     
26049 };
26050
26051
26052 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
26053
26054
26055      /**
26056      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
26057      */
26058     
26059     owner : false,
26060     
26061      /**
26062      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26063      *                        Roo.resizable.
26064      */
26065     resizable : false,
26066      /**
26067      * @cfg {Number} height (in pixels)
26068      */   
26069     height: 300,
26070    /**
26071      * @cfg {Number} width (in pixels)
26072      */   
26073     width: 500,
26074     
26075     /**
26076      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26077      * 
26078      */
26079     stylesheets: false,
26080     
26081     /**
26082      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26083      */
26084     allowComments: false,
26085     // id of frame..
26086     frameId: false,
26087     
26088     // private properties
26089     validationEvent : false,
26090     deferHeight: true,
26091     initialized : false,
26092     activated : false,
26093     sourceEditMode : false,
26094     onFocus : Roo.emptyFn,
26095     iframePad:3,
26096     hideMode:'offsets',
26097     
26098     clearUp: true,
26099     
26100     // blacklist + whitelisted elements..
26101     black: false,
26102     white: false,
26103      
26104     bodyCls : '',
26105
26106     /**
26107      * Protected method that will not generally be called directly. It
26108      * is called when the editor initializes the iframe with HTML contents. Override this method if you
26109      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26110      */
26111     getDocMarkup : function(){
26112         // body styles..
26113         var st = '';
26114         
26115         // inherit styels from page...?? 
26116         if (this.stylesheets === false) {
26117             
26118             Roo.get(document.head).select('style').each(function(node) {
26119                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26120             });
26121             
26122             Roo.get(document.head).select('link').each(function(node) { 
26123                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26124             });
26125             
26126         } else if (!this.stylesheets.length) {
26127                 // simple..
26128                 st = '<style type="text/css">' +
26129                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26130                    '</style>';
26131         } else {
26132             for (var i in this.stylesheets) { 
26133                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26134             }
26135             
26136         }
26137         
26138         st +=  '<style type="text/css">' +
26139             'IMG { cursor: pointer } ' +
26140         '</style>';
26141
26142         var cls = 'roo-htmleditor-body';
26143         
26144         if(this.bodyCls.length){
26145             cls += ' ' + this.bodyCls;
26146         }
26147         
26148         return '<html><head>' + st  +
26149             //<style type="text/css">' +
26150             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26151             //'</style>' +
26152             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
26153     },
26154
26155     // private
26156     onRender : function(ct, position)
26157     {
26158         var _t = this;
26159         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26160         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26161         
26162         
26163         this.el.dom.style.border = '0 none';
26164         this.el.dom.setAttribute('tabIndex', -1);
26165         this.el.addClass('x-hidden hide');
26166         
26167         
26168         
26169         if(Roo.isIE){ // fix IE 1px bogus margin
26170             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26171         }
26172        
26173         
26174         this.frameId = Roo.id();
26175         
26176          
26177         
26178         var iframe = this.owner.wrap.createChild({
26179             tag: 'iframe',
26180             cls: 'form-control', // bootstrap..
26181             id: this.frameId,
26182             name: this.frameId,
26183             frameBorder : 'no',
26184             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
26185         }, this.el
26186         );
26187         
26188         
26189         this.iframe = iframe.dom;
26190
26191          this.assignDocWin();
26192         
26193         this.doc.designMode = 'on';
26194        
26195         this.doc.open();
26196         this.doc.write(this.getDocMarkup());
26197         this.doc.close();
26198
26199         
26200         var task = { // must defer to wait for browser to be ready
26201             run : function(){
26202                 //console.log("run task?" + this.doc.readyState);
26203                 this.assignDocWin();
26204                 if(this.doc.body || this.doc.readyState == 'complete'){
26205                     try {
26206                         this.doc.designMode="on";
26207                     } catch (e) {
26208                         return;
26209                     }
26210                     Roo.TaskMgr.stop(task);
26211                     this.initEditor.defer(10, this);
26212                 }
26213             },
26214             interval : 10,
26215             duration: 10000,
26216             scope: this
26217         };
26218         Roo.TaskMgr.start(task);
26219
26220     },
26221
26222     // private
26223     onResize : function(w, h)
26224     {
26225          Roo.log('resize: ' +w + ',' + h );
26226         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26227         if(!this.iframe){
26228             return;
26229         }
26230         if(typeof w == 'number'){
26231             
26232             this.iframe.style.width = w + 'px';
26233         }
26234         if(typeof h == 'number'){
26235             
26236             this.iframe.style.height = h + 'px';
26237             if(this.doc){
26238                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26239             }
26240         }
26241         
26242     },
26243
26244     /**
26245      * Toggles the editor between standard and source edit mode.
26246      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26247      */
26248     toggleSourceEdit : function(sourceEditMode){
26249         
26250         this.sourceEditMode = sourceEditMode === true;
26251         
26252         if(this.sourceEditMode){
26253  
26254             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
26255             
26256         }else{
26257             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26258             //this.iframe.className = '';
26259             this.deferFocus();
26260         }
26261         //this.setSize(this.owner.wrap.getSize());
26262         //this.fireEvent('editmodechange', this, this.sourceEditMode);
26263     },
26264
26265     
26266   
26267
26268     /**
26269      * Protected method that will not generally be called directly. If you need/want
26270      * custom HTML cleanup, this is the method you should override.
26271      * @param {String} html The HTML to be cleaned
26272      * return {String} The cleaned HTML
26273      */
26274     cleanHtml : function(html){
26275         html = String(html);
26276         if(html.length > 5){
26277             if(Roo.isSafari){ // strip safari nonsense
26278                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26279             }
26280         }
26281         if(html == '&nbsp;'){
26282             html = '';
26283         }
26284         return html;
26285     },
26286
26287     /**
26288      * HTML Editor -> Textarea
26289      * Protected method that will not generally be called directly. Syncs the contents
26290      * of the editor iframe with the textarea.
26291      */
26292     syncValue : function(){
26293         if(this.initialized){
26294             var bd = (this.doc.body || this.doc.documentElement);
26295             //this.cleanUpPaste(); -- this is done else where and causes havoc..
26296             var html = bd.innerHTML;
26297             if(Roo.isSafari){
26298                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26299                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26300                 if(m && m[1]){
26301                     html = '<div style="'+m[0]+'">' + html + '</div>';
26302                 }
26303             }
26304             html = this.cleanHtml(html);
26305             // fix up the special chars.. normaly like back quotes in word...
26306             // however we do not want to do this with chinese..
26307             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26308                 
26309                 var cc = match.charCodeAt();
26310
26311                 // Get the character value, handling surrogate pairs
26312                 if (match.length == 2) {
26313                     // It's a surrogate pair, calculate the Unicode code point
26314                     var high = match.charCodeAt(0) - 0xD800;
26315                     var low  = match.charCodeAt(1) - 0xDC00;
26316                     cc = (high * 0x400) + low + 0x10000;
26317                 }  else if (
26318                     (cc >= 0x4E00 && cc < 0xA000 ) ||
26319                     (cc >= 0x3400 && cc < 0x4E00 ) ||
26320                     (cc >= 0xf900 && cc < 0xfb00 )
26321                 ) {
26322                         return match;
26323                 }  
26324          
26325                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26326                 return "&#" + cc + ";";
26327                 
26328                 
26329             });
26330             
26331             
26332              
26333             if(this.owner.fireEvent('beforesync', this, html) !== false){
26334                 this.el.dom.value = html;
26335                 this.owner.fireEvent('sync', this, html);
26336             }
26337         }
26338     },
26339
26340     /**
26341      * Protected method that will not generally be called directly. Pushes the value of the textarea
26342      * into the iframe editor.
26343      */
26344     pushValue : function(){
26345         if(this.initialized){
26346             var v = this.el.dom.value.trim();
26347             
26348 //            if(v.length < 1){
26349 //                v = '&#160;';
26350 //            }
26351             
26352             if(this.owner.fireEvent('beforepush', this, v) !== false){
26353                 var d = (this.doc.body || this.doc.documentElement);
26354                 d.innerHTML = v;
26355                 this.cleanUpPaste();
26356                 this.el.dom.value = d.innerHTML;
26357                 this.owner.fireEvent('push', this, v);
26358             }
26359         }
26360     },
26361
26362     // private
26363     deferFocus : function(){
26364         this.focus.defer(10, this);
26365     },
26366
26367     // doc'ed in Field
26368     focus : function(){
26369         if(this.win && !this.sourceEditMode){
26370             this.win.focus();
26371         }else{
26372             this.el.focus();
26373         }
26374     },
26375     
26376     assignDocWin: function()
26377     {
26378         var iframe = this.iframe;
26379         
26380          if(Roo.isIE){
26381             this.doc = iframe.contentWindow.document;
26382             this.win = iframe.contentWindow;
26383         } else {
26384 //            if (!Roo.get(this.frameId)) {
26385 //                return;
26386 //            }
26387 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26388 //            this.win = Roo.get(this.frameId).dom.contentWindow;
26389             
26390             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26391                 return;
26392             }
26393             
26394             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26395             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26396         }
26397     },
26398     
26399     // private
26400     initEditor : function(){
26401         //console.log("INIT EDITOR");
26402         this.assignDocWin();
26403         
26404         
26405         
26406         this.doc.designMode="on";
26407         this.doc.open();
26408         this.doc.write(this.getDocMarkup());
26409         this.doc.close();
26410         
26411         var dbody = (this.doc.body || this.doc.documentElement);
26412         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26413         // this copies styles from the containing element into thsi one..
26414         // not sure why we need all of this..
26415         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26416         
26417         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26418         //ss['background-attachment'] = 'fixed'; // w3c
26419         dbody.bgProperties = 'fixed'; // ie
26420         //Roo.DomHelper.applyStyles(dbody, ss);
26421         Roo.EventManager.on(this.doc, {
26422             //'mousedown': this.onEditorEvent,
26423             'mouseup': this.onEditorEvent,
26424             'dblclick': this.onEditorEvent,
26425             'click': this.onEditorEvent,
26426             'keyup': this.onEditorEvent,
26427             buffer:100,
26428             scope: this
26429         });
26430         if(Roo.isGecko){
26431             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26432         }
26433         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26434             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26435         }
26436         this.initialized = true;
26437
26438         this.owner.fireEvent('initialize', this);
26439         this.pushValue();
26440     },
26441
26442     // private
26443     onDestroy : function(){
26444         
26445         
26446         
26447         if(this.rendered){
26448             
26449             //for (var i =0; i < this.toolbars.length;i++) {
26450             //    // fixme - ask toolbars for heights?
26451             //    this.toolbars[i].onDestroy();
26452            // }
26453             
26454             //this.wrap.dom.innerHTML = '';
26455             //this.wrap.remove();
26456         }
26457     },
26458
26459     // private
26460     onFirstFocus : function(){
26461         
26462         this.assignDocWin();
26463         
26464         
26465         this.activated = true;
26466          
26467     
26468         if(Roo.isGecko){ // prevent silly gecko errors
26469             this.win.focus();
26470             var s = this.win.getSelection();
26471             if(!s.focusNode || s.focusNode.nodeType != 3){
26472                 var r = s.getRangeAt(0);
26473                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26474                 r.collapse(true);
26475                 this.deferFocus();
26476             }
26477             try{
26478                 this.execCmd('useCSS', true);
26479                 this.execCmd('styleWithCSS', false);
26480             }catch(e){}
26481         }
26482         this.owner.fireEvent('activate', this);
26483     },
26484
26485     // private
26486     adjustFont: function(btn){
26487         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26488         //if(Roo.isSafari){ // safari
26489         //    adjust *= 2;
26490        // }
26491         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26492         if(Roo.isSafari){ // safari
26493             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26494             v =  (v < 10) ? 10 : v;
26495             v =  (v > 48) ? 48 : v;
26496             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26497             
26498         }
26499         
26500         
26501         v = Math.max(1, v+adjust);
26502         
26503         this.execCmd('FontSize', v  );
26504     },
26505
26506     onEditorEvent : function(e)
26507     {
26508         this.owner.fireEvent('editorevent', this, e);
26509       //  this.updateToolbar();
26510         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26511     },
26512
26513     insertTag : function(tg)
26514     {
26515         // could be a bit smarter... -> wrap the current selected tRoo..
26516         if (tg.toLowerCase() == 'span' ||
26517             tg.toLowerCase() == 'code' ||
26518             tg.toLowerCase() == 'sup' ||
26519             tg.toLowerCase() == 'sub' 
26520             ) {
26521             
26522             range = this.createRange(this.getSelection());
26523             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26524             wrappingNode.appendChild(range.extractContents());
26525             range.insertNode(wrappingNode);
26526
26527             return;
26528             
26529             
26530             
26531         }
26532         this.execCmd("formatblock",   tg);
26533         
26534     },
26535     
26536     insertText : function(txt)
26537     {
26538         
26539         
26540         var range = this.createRange();
26541         range.deleteContents();
26542                //alert(Sender.getAttribute('label'));
26543                
26544         range.insertNode(this.doc.createTextNode(txt));
26545     } ,
26546     
26547      
26548
26549     /**
26550      * Executes a Midas editor command on the editor document and performs necessary focus and
26551      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26552      * @param {String} cmd The Midas command
26553      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26554      */
26555     relayCmd : function(cmd, value){
26556         this.win.focus();
26557         this.execCmd(cmd, value);
26558         this.owner.fireEvent('editorevent', this);
26559         //this.updateToolbar();
26560         this.owner.deferFocus();
26561     },
26562
26563     /**
26564      * Executes a Midas editor command directly on the editor document.
26565      * For visual commands, you should use {@link #relayCmd} instead.
26566      * <b>This should only be called after the editor is initialized.</b>
26567      * @param {String} cmd The Midas command
26568      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26569      */
26570     execCmd : function(cmd, value){
26571         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26572         this.syncValue();
26573     },
26574  
26575  
26576    
26577     /**
26578      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26579      * to insert tRoo.
26580      * @param {String} text | dom node.. 
26581      */
26582     insertAtCursor : function(text)
26583     {
26584         
26585         if(!this.activated){
26586             return;
26587         }
26588         /*
26589         if(Roo.isIE){
26590             this.win.focus();
26591             var r = this.doc.selection.createRange();
26592             if(r){
26593                 r.collapse(true);
26594                 r.pasteHTML(text);
26595                 this.syncValue();
26596                 this.deferFocus();
26597             
26598             }
26599             return;
26600         }
26601         */
26602         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26603             this.win.focus();
26604             
26605             
26606             // from jquery ui (MIT licenced)
26607             var range, node;
26608             var win = this.win;
26609             
26610             if (win.getSelection && win.getSelection().getRangeAt) {
26611                 range = win.getSelection().getRangeAt(0);
26612                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26613                 range.insertNode(node);
26614             } else if (win.document.selection && win.document.selection.createRange) {
26615                 // no firefox support
26616                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26617                 win.document.selection.createRange().pasteHTML(txt);
26618             } else {
26619                 // no firefox support
26620                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26621                 this.execCmd('InsertHTML', txt);
26622             } 
26623             
26624             this.syncValue();
26625             
26626             this.deferFocus();
26627         }
26628     },
26629  // private
26630     mozKeyPress : function(e){
26631         if(e.ctrlKey){
26632             var c = e.getCharCode(), cmd;
26633           
26634             if(c > 0){
26635                 c = String.fromCharCode(c).toLowerCase();
26636                 switch(c){
26637                     case 'b':
26638                         cmd = 'bold';
26639                         break;
26640                     case 'i':
26641                         cmd = 'italic';
26642                         break;
26643                     
26644                     case 'u':
26645                         cmd = 'underline';
26646                         break;
26647                     
26648                     case 'v':
26649                         this.cleanUpPaste.defer(100, this);
26650                         return;
26651                         
26652                 }
26653                 if(cmd){
26654                     this.win.focus();
26655                     this.execCmd(cmd);
26656                     this.deferFocus();
26657                     e.preventDefault();
26658                 }
26659                 
26660             }
26661         }
26662     },
26663
26664     // private
26665     fixKeys : function(){ // load time branching for fastest keydown performance
26666         if(Roo.isIE){
26667             return function(e){
26668                 var k = e.getKey(), r;
26669                 if(k == e.TAB){
26670                     e.stopEvent();
26671                     r = this.doc.selection.createRange();
26672                     if(r){
26673                         r.collapse(true);
26674                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26675                         this.deferFocus();
26676                     }
26677                     return;
26678                 }
26679                 
26680                 if(k == e.ENTER){
26681                     r = this.doc.selection.createRange();
26682                     if(r){
26683                         var target = r.parentElement();
26684                         if(!target || target.tagName.toLowerCase() != 'li'){
26685                             e.stopEvent();
26686                             r.pasteHTML('<br />');
26687                             r.collapse(false);
26688                             r.select();
26689                         }
26690                     }
26691                 }
26692                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26693                     this.cleanUpPaste.defer(100, this);
26694                     return;
26695                 }
26696                 
26697                 
26698             };
26699         }else if(Roo.isOpera){
26700             return function(e){
26701                 var k = e.getKey();
26702                 if(k == e.TAB){
26703                     e.stopEvent();
26704                     this.win.focus();
26705                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26706                     this.deferFocus();
26707                 }
26708                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26709                     this.cleanUpPaste.defer(100, this);
26710                     return;
26711                 }
26712                 
26713             };
26714         }else if(Roo.isSafari){
26715             return function(e){
26716                 var k = e.getKey();
26717                 
26718                 if(k == e.TAB){
26719                     e.stopEvent();
26720                     this.execCmd('InsertText','\t');
26721                     this.deferFocus();
26722                     return;
26723                 }
26724                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26725                     this.cleanUpPaste.defer(100, this);
26726                     return;
26727                 }
26728                 
26729              };
26730         }
26731     }(),
26732     
26733     getAllAncestors: function()
26734     {
26735         var p = this.getSelectedNode();
26736         var a = [];
26737         if (!p) {
26738             a.push(p); // push blank onto stack..
26739             p = this.getParentElement();
26740         }
26741         
26742         
26743         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26744             a.push(p);
26745             p = p.parentNode;
26746         }
26747         a.push(this.doc.body);
26748         return a;
26749     },
26750     lastSel : false,
26751     lastSelNode : false,
26752     
26753     
26754     getSelection : function() 
26755     {
26756         this.assignDocWin();
26757         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26758     },
26759     
26760     getSelectedNode: function() 
26761     {
26762         // this may only work on Gecko!!!
26763         
26764         // should we cache this!!!!
26765         
26766         
26767         
26768          
26769         var range = this.createRange(this.getSelection()).cloneRange();
26770         
26771         if (Roo.isIE) {
26772             var parent = range.parentElement();
26773             while (true) {
26774                 var testRange = range.duplicate();
26775                 testRange.moveToElementText(parent);
26776                 if (testRange.inRange(range)) {
26777                     break;
26778                 }
26779                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26780                     break;
26781                 }
26782                 parent = parent.parentElement;
26783             }
26784             return parent;
26785         }
26786         
26787         // is ancestor a text element.
26788         var ac =  range.commonAncestorContainer;
26789         if (ac.nodeType == 3) {
26790             ac = ac.parentNode;
26791         }
26792         
26793         var ar = ac.childNodes;
26794          
26795         var nodes = [];
26796         var other_nodes = [];
26797         var has_other_nodes = false;
26798         for (var i=0;i<ar.length;i++) {
26799             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26800                 continue;
26801             }
26802             // fullly contained node.
26803             
26804             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26805                 nodes.push(ar[i]);
26806                 continue;
26807             }
26808             
26809             // probably selected..
26810             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26811                 other_nodes.push(ar[i]);
26812                 continue;
26813             }
26814             // outer..
26815             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26816                 continue;
26817             }
26818             
26819             
26820             has_other_nodes = true;
26821         }
26822         if (!nodes.length && other_nodes.length) {
26823             nodes= other_nodes;
26824         }
26825         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26826             return false;
26827         }
26828         
26829         return nodes[0];
26830     },
26831     createRange: function(sel)
26832     {
26833         // this has strange effects when using with 
26834         // top toolbar - not sure if it's a great idea.
26835         //this.editor.contentWindow.focus();
26836         if (typeof sel != "undefined") {
26837             try {
26838                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26839             } catch(e) {
26840                 return this.doc.createRange();
26841             }
26842         } else {
26843             return this.doc.createRange();
26844         }
26845     },
26846     getParentElement: function()
26847     {
26848         
26849         this.assignDocWin();
26850         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26851         
26852         var range = this.createRange(sel);
26853          
26854         try {
26855             var p = range.commonAncestorContainer;
26856             while (p.nodeType == 3) { // text node
26857                 p = p.parentNode;
26858             }
26859             return p;
26860         } catch (e) {
26861             return null;
26862         }
26863     
26864     },
26865     /***
26866      *
26867      * Range intersection.. the hard stuff...
26868      *  '-1' = before
26869      *  '0' = hits..
26870      *  '1' = after.
26871      *         [ -- selected range --- ]
26872      *   [fail]                        [fail]
26873      *
26874      *    basically..
26875      *      if end is before start or  hits it. fail.
26876      *      if start is after end or hits it fail.
26877      *
26878      *   if either hits (but other is outside. - then it's not 
26879      *   
26880      *    
26881      **/
26882     
26883     
26884     // @see http://www.thismuchiknow.co.uk/?p=64.
26885     rangeIntersectsNode : function(range, node)
26886     {
26887         var nodeRange = node.ownerDocument.createRange();
26888         try {
26889             nodeRange.selectNode(node);
26890         } catch (e) {
26891             nodeRange.selectNodeContents(node);
26892         }
26893     
26894         var rangeStartRange = range.cloneRange();
26895         rangeStartRange.collapse(true);
26896     
26897         var rangeEndRange = range.cloneRange();
26898         rangeEndRange.collapse(false);
26899     
26900         var nodeStartRange = nodeRange.cloneRange();
26901         nodeStartRange.collapse(true);
26902     
26903         var nodeEndRange = nodeRange.cloneRange();
26904         nodeEndRange.collapse(false);
26905     
26906         return rangeStartRange.compareBoundaryPoints(
26907                  Range.START_TO_START, nodeEndRange) == -1 &&
26908                rangeEndRange.compareBoundaryPoints(
26909                  Range.START_TO_START, nodeStartRange) == 1;
26910         
26911          
26912     },
26913     rangeCompareNode : function(range, node)
26914     {
26915         var nodeRange = node.ownerDocument.createRange();
26916         try {
26917             nodeRange.selectNode(node);
26918         } catch (e) {
26919             nodeRange.selectNodeContents(node);
26920         }
26921         
26922         
26923         range.collapse(true);
26924     
26925         nodeRange.collapse(true);
26926      
26927         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26928         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26929          
26930         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26931         
26932         var nodeIsBefore   =  ss == 1;
26933         var nodeIsAfter    = ee == -1;
26934         
26935         if (nodeIsBefore && nodeIsAfter) {
26936             return 0; // outer
26937         }
26938         if (!nodeIsBefore && nodeIsAfter) {
26939             return 1; //right trailed.
26940         }
26941         
26942         if (nodeIsBefore && !nodeIsAfter) {
26943             return 2;  // left trailed.
26944         }
26945         // fully contined.
26946         return 3;
26947     },
26948
26949     // private? - in a new class?
26950     cleanUpPaste :  function()
26951     {
26952         // cleans up the whole document..
26953         Roo.log('cleanuppaste');
26954         
26955         this.cleanUpChildren(this.doc.body);
26956         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26957         if (clean != this.doc.body.innerHTML) {
26958             this.doc.body.innerHTML = clean;
26959         }
26960         
26961     },
26962     
26963     cleanWordChars : function(input) {// change the chars to hex code
26964         var he = Roo.HtmlEditorCore;
26965         
26966         var output = input;
26967         Roo.each(he.swapCodes, function(sw) { 
26968             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26969             
26970             output = output.replace(swapper, sw[1]);
26971         });
26972         
26973         return output;
26974     },
26975     
26976     
26977     cleanUpChildren : function (n)
26978     {
26979         if (!n.childNodes.length) {
26980             return;
26981         }
26982         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26983            this.cleanUpChild(n.childNodes[i]);
26984         }
26985     },
26986     
26987     
26988         
26989     
26990     cleanUpChild : function (node)
26991     {
26992         var ed = this;
26993         //console.log(node);
26994         if (node.nodeName == "#text") {
26995             // clean up silly Windows -- stuff?
26996             return; 
26997         }
26998         if (node.nodeName == "#comment") {
26999             if (!this.allowComments) {
27000                 node.parentNode.removeChild(node);
27001             }
27002             // clean up silly Windows -- stuff?
27003             return; 
27004         }
27005         var lcname = node.tagName.toLowerCase();
27006         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
27007         // whitelist of tags..
27008         
27009         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
27010             // remove node.
27011             node.parentNode.removeChild(node);
27012             return;
27013             
27014         }
27015         
27016         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27017         
27018         // spans with no attributes - just remove them..
27019         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
27020             remove_keep_children = true;
27021         }
27022         
27023         // remove <a name=....> as rendering on yahoo mailer is borked with this.
27024         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27025         
27026         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27027         //    remove_keep_children = true;
27028         //}
27029         
27030         if (remove_keep_children) {
27031             this.cleanUpChildren(node);
27032             // inserts everything just before this node...
27033             while (node.childNodes.length) {
27034                 var cn = node.childNodes[0];
27035                 node.removeChild(cn);
27036                 node.parentNode.insertBefore(cn, node);
27037             }
27038             node.parentNode.removeChild(node);
27039             return;
27040         }
27041         
27042         if (!node.attributes || !node.attributes.length) {
27043             
27044           
27045             
27046             
27047             this.cleanUpChildren(node);
27048             return;
27049         }
27050         
27051         function cleanAttr(n,v)
27052         {
27053             
27054             if (v.match(/^\./) || v.match(/^\//)) {
27055                 return;
27056             }
27057             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27058                 return;
27059             }
27060             if (v.match(/^#/)) {
27061                 return;
27062             }
27063             if (v.match(/^\{/)) { // allow template editing.
27064                 return;
27065             }
27066 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27067             node.removeAttribute(n);
27068             
27069         }
27070         
27071         var cwhite = this.cwhite;
27072         var cblack = this.cblack;
27073             
27074         function cleanStyle(n,v)
27075         {
27076             if (v.match(/expression/)) { //XSS?? should we even bother..
27077                 node.removeAttribute(n);
27078                 return;
27079             }
27080             
27081             var parts = v.split(/;/);
27082             var clean = [];
27083             
27084             Roo.each(parts, function(p) {
27085                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27086                 if (!p.length) {
27087                     return true;
27088                 }
27089                 var l = p.split(':').shift().replace(/\s+/g,'');
27090                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27091                 
27092                 if ( cwhite.length && cblack.indexOf(l) > -1) {
27093 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27094                     //node.removeAttribute(n);
27095                     return true;
27096                 }
27097                 //Roo.log()
27098                 // only allow 'c whitelisted system attributes'
27099                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
27100 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27101                     //node.removeAttribute(n);
27102                     return true;
27103                 }
27104                 
27105                 
27106                  
27107                 
27108                 clean.push(p);
27109                 return true;
27110             });
27111             if (clean.length) { 
27112                 node.setAttribute(n, clean.join(';'));
27113             } else {
27114                 node.removeAttribute(n);
27115             }
27116             
27117         }
27118         
27119         
27120         for (var i = node.attributes.length-1; i > -1 ; i--) {
27121             var a = node.attributes[i];
27122             //console.log(a);
27123             
27124             if (a.name.toLowerCase().substr(0,2)=='on')  {
27125                 node.removeAttribute(a.name);
27126                 continue;
27127             }
27128             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27129                 node.removeAttribute(a.name);
27130                 continue;
27131             }
27132             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27133                 cleanAttr(a.name,a.value); // fixme..
27134                 continue;
27135             }
27136             if (a.name == 'style') {
27137                 cleanStyle(a.name,a.value);
27138                 continue;
27139             }
27140             /// clean up MS crap..
27141             // tecnically this should be a list of valid class'es..
27142             
27143             
27144             if (a.name == 'class') {
27145                 if (a.value.match(/^Mso/)) {
27146                     node.removeAttribute('class');
27147                 }
27148                 
27149                 if (a.value.match(/^body$/)) {
27150                     node.removeAttribute('class');
27151                 }
27152                 continue;
27153             }
27154             
27155             // style cleanup!?
27156             // class cleanup?
27157             
27158         }
27159         
27160         
27161         this.cleanUpChildren(node);
27162         
27163         
27164     },
27165     
27166     /**
27167      * Clean up MS wordisms...
27168      */
27169     cleanWord : function(node)
27170     {
27171         if (!node) {
27172             this.cleanWord(this.doc.body);
27173             return;
27174         }
27175         
27176         if(
27177                 node.nodeName == 'SPAN' &&
27178                 !node.hasAttributes() &&
27179                 node.childNodes.length == 1 &&
27180                 node.firstChild.nodeName == "#text"  
27181         ) {
27182             var textNode = node.firstChild;
27183             node.removeChild(textNode);
27184             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27185                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27186             }
27187             node.parentNode.insertBefore(textNode, node);
27188             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27189                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27190             }
27191             node.parentNode.removeChild(node);
27192         }
27193         
27194         if (node.nodeName == "#text") {
27195             // clean up silly Windows -- stuff?
27196             return; 
27197         }
27198         if (node.nodeName == "#comment") {
27199             node.parentNode.removeChild(node);
27200             // clean up silly Windows -- stuff?
27201             return; 
27202         }
27203         
27204         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27205             node.parentNode.removeChild(node);
27206             return;
27207         }
27208         //Roo.log(node.tagName);
27209         // remove - but keep children..
27210         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27211             //Roo.log('-- removed');
27212             while (node.childNodes.length) {
27213                 var cn = node.childNodes[0];
27214                 node.removeChild(cn);
27215                 node.parentNode.insertBefore(cn, node);
27216                 // move node to parent - and clean it..
27217                 this.cleanWord(cn);
27218             }
27219             node.parentNode.removeChild(node);
27220             /// no need to iterate chidlren = it's got none..
27221             //this.iterateChildren(node, this.cleanWord);
27222             return;
27223         }
27224         // clean styles
27225         if (node.className.length) {
27226             
27227             var cn = node.className.split(/\W+/);
27228             var cna = [];
27229             Roo.each(cn, function(cls) {
27230                 if (cls.match(/Mso[a-zA-Z]+/)) {
27231                     return;
27232                 }
27233                 cna.push(cls);
27234             });
27235             node.className = cna.length ? cna.join(' ') : '';
27236             if (!cna.length) {
27237                 node.removeAttribute("class");
27238             }
27239         }
27240         
27241         if (node.hasAttribute("lang")) {
27242             node.removeAttribute("lang");
27243         }
27244         
27245         if (node.hasAttribute("style")) {
27246             
27247             var styles = node.getAttribute("style").split(";");
27248             var nstyle = [];
27249             Roo.each(styles, function(s) {
27250                 if (!s.match(/:/)) {
27251                     return;
27252                 }
27253                 var kv = s.split(":");
27254                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27255                     return;
27256                 }
27257                 // what ever is left... we allow.
27258                 nstyle.push(s);
27259             });
27260             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27261             if (!nstyle.length) {
27262                 node.removeAttribute('style');
27263             }
27264         }
27265         this.iterateChildren(node, this.cleanWord);
27266         
27267         
27268         
27269     },
27270     /**
27271      * iterateChildren of a Node, calling fn each time, using this as the scole..
27272      * @param {DomNode} node node to iterate children of.
27273      * @param {Function} fn method of this class to call on each item.
27274      */
27275     iterateChildren : function(node, fn)
27276     {
27277         if (!node.childNodes.length) {
27278                 return;
27279         }
27280         for (var i = node.childNodes.length-1; i > -1 ; i--) {
27281            fn.call(this, node.childNodes[i])
27282         }
27283     },
27284     
27285     
27286     /**
27287      * cleanTableWidths.
27288      *
27289      * Quite often pasting from word etc.. results in tables with column and widths.
27290      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27291      *
27292      */
27293     cleanTableWidths : function(node)
27294     {
27295          
27296          
27297         if (!node) {
27298             this.cleanTableWidths(this.doc.body);
27299             return;
27300         }
27301         
27302         // ignore list...
27303         if (node.nodeName == "#text" || node.nodeName == "#comment") {
27304             return; 
27305         }
27306         Roo.log(node.tagName);
27307         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27308             this.iterateChildren(node, this.cleanTableWidths);
27309             return;
27310         }
27311         if (node.hasAttribute('width')) {
27312             node.removeAttribute('width');
27313         }
27314         
27315          
27316         if (node.hasAttribute("style")) {
27317             // pretty basic...
27318             
27319             var styles = node.getAttribute("style").split(";");
27320             var nstyle = [];
27321             Roo.each(styles, function(s) {
27322                 if (!s.match(/:/)) {
27323                     return;
27324                 }
27325                 var kv = s.split(":");
27326                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27327                     return;
27328                 }
27329                 // what ever is left... we allow.
27330                 nstyle.push(s);
27331             });
27332             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27333             if (!nstyle.length) {
27334                 node.removeAttribute('style');
27335             }
27336         }
27337         
27338         this.iterateChildren(node, this.cleanTableWidths);
27339         
27340         
27341     },
27342     
27343     
27344     
27345     
27346     domToHTML : function(currentElement, depth, nopadtext) {
27347         
27348         depth = depth || 0;
27349         nopadtext = nopadtext || false;
27350     
27351         if (!currentElement) {
27352             return this.domToHTML(this.doc.body);
27353         }
27354         
27355         //Roo.log(currentElement);
27356         var j;
27357         var allText = false;
27358         var nodeName = currentElement.nodeName;
27359         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27360         
27361         if  (nodeName == '#text') {
27362             
27363             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27364         }
27365         
27366         
27367         var ret = '';
27368         if (nodeName != 'BODY') {
27369              
27370             var i = 0;
27371             // Prints the node tagName, such as <A>, <IMG>, etc
27372             if (tagName) {
27373                 var attr = [];
27374                 for(i = 0; i < currentElement.attributes.length;i++) {
27375                     // quoting?
27376                     var aname = currentElement.attributes.item(i).name;
27377                     if (!currentElement.attributes.item(i).value.length) {
27378                         continue;
27379                     }
27380                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27381                 }
27382                 
27383                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27384             } 
27385             else {
27386                 
27387                 // eack
27388             }
27389         } else {
27390             tagName = false;
27391         }
27392         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27393             return ret;
27394         }
27395         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27396             nopadtext = true;
27397         }
27398         
27399         
27400         // Traverse the tree
27401         i = 0;
27402         var currentElementChild = currentElement.childNodes.item(i);
27403         var allText = true;
27404         var innerHTML  = '';
27405         lastnode = '';
27406         while (currentElementChild) {
27407             // Formatting code (indent the tree so it looks nice on the screen)
27408             var nopad = nopadtext;
27409             if (lastnode == 'SPAN') {
27410                 nopad  = true;
27411             }
27412             // text
27413             if  (currentElementChild.nodeName == '#text') {
27414                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27415                 toadd = nopadtext ? toadd : toadd.trim();
27416                 if (!nopad && toadd.length > 80) {
27417                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
27418                 }
27419                 innerHTML  += toadd;
27420                 
27421                 i++;
27422                 currentElementChild = currentElement.childNodes.item(i);
27423                 lastNode = '';
27424                 continue;
27425             }
27426             allText = false;
27427             
27428             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
27429                 
27430             // Recursively traverse the tree structure of the child node
27431             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
27432             lastnode = currentElementChild.nodeName;
27433             i++;
27434             currentElementChild=currentElement.childNodes.item(i);
27435         }
27436         
27437         ret += innerHTML;
27438         
27439         if (!allText) {
27440                 // The remaining code is mostly for formatting the tree
27441             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27442         }
27443         
27444         
27445         if (tagName) {
27446             ret+= "</"+tagName+">";
27447         }
27448         return ret;
27449         
27450     },
27451         
27452     applyBlacklists : function()
27453     {
27454         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27455         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27456         
27457         this.white = [];
27458         this.black = [];
27459         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27460             if (b.indexOf(tag) > -1) {
27461                 return;
27462             }
27463             this.white.push(tag);
27464             
27465         }, this);
27466         
27467         Roo.each(w, function(tag) {
27468             if (b.indexOf(tag) > -1) {
27469                 return;
27470             }
27471             if (this.white.indexOf(tag) > -1) {
27472                 return;
27473             }
27474             this.white.push(tag);
27475             
27476         }, this);
27477         
27478         
27479         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27480             if (w.indexOf(tag) > -1) {
27481                 return;
27482             }
27483             this.black.push(tag);
27484             
27485         }, this);
27486         
27487         Roo.each(b, function(tag) {
27488             if (w.indexOf(tag) > -1) {
27489                 return;
27490             }
27491             if (this.black.indexOf(tag) > -1) {
27492                 return;
27493             }
27494             this.black.push(tag);
27495             
27496         }, this);
27497         
27498         
27499         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27500         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27501         
27502         this.cwhite = [];
27503         this.cblack = [];
27504         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27505             if (b.indexOf(tag) > -1) {
27506                 return;
27507             }
27508             this.cwhite.push(tag);
27509             
27510         }, this);
27511         
27512         Roo.each(w, function(tag) {
27513             if (b.indexOf(tag) > -1) {
27514                 return;
27515             }
27516             if (this.cwhite.indexOf(tag) > -1) {
27517                 return;
27518             }
27519             this.cwhite.push(tag);
27520             
27521         }, this);
27522         
27523         
27524         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27525             if (w.indexOf(tag) > -1) {
27526                 return;
27527             }
27528             this.cblack.push(tag);
27529             
27530         }, this);
27531         
27532         Roo.each(b, function(tag) {
27533             if (w.indexOf(tag) > -1) {
27534                 return;
27535             }
27536             if (this.cblack.indexOf(tag) > -1) {
27537                 return;
27538             }
27539             this.cblack.push(tag);
27540             
27541         }, this);
27542     },
27543     
27544     setStylesheets : function(stylesheets)
27545     {
27546         if(typeof(stylesheets) == 'string'){
27547             Roo.get(this.iframe.contentDocument.head).createChild({
27548                 tag : 'link',
27549                 rel : 'stylesheet',
27550                 type : 'text/css',
27551                 href : stylesheets
27552             });
27553             
27554             return;
27555         }
27556         var _this = this;
27557      
27558         Roo.each(stylesheets, function(s) {
27559             if(!s.length){
27560                 return;
27561             }
27562             
27563             Roo.get(_this.iframe.contentDocument.head).createChild({
27564                 tag : 'link',
27565                 rel : 'stylesheet',
27566                 type : 'text/css',
27567                 href : s
27568             });
27569         });
27570
27571         
27572     },
27573     
27574     removeStylesheets : function()
27575     {
27576         var _this = this;
27577         
27578         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27579             s.remove();
27580         });
27581     },
27582     
27583     setStyle : function(style)
27584     {
27585         Roo.get(this.iframe.contentDocument.head).createChild({
27586             tag : 'style',
27587             type : 'text/css',
27588             html : style
27589         });
27590
27591         return;
27592     }
27593     
27594     // hide stuff that is not compatible
27595     /**
27596      * @event blur
27597      * @hide
27598      */
27599     /**
27600      * @event change
27601      * @hide
27602      */
27603     /**
27604      * @event focus
27605      * @hide
27606      */
27607     /**
27608      * @event specialkey
27609      * @hide
27610      */
27611     /**
27612      * @cfg {String} fieldClass @hide
27613      */
27614     /**
27615      * @cfg {String} focusClass @hide
27616      */
27617     /**
27618      * @cfg {String} autoCreate @hide
27619      */
27620     /**
27621      * @cfg {String} inputType @hide
27622      */
27623     /**
27624      * @cfg {String} invalidClass @hide
27625      */
27626     /**
27627      * @cfg {String} invalidText @hide
27628      */
27629     /**
27630      * @cfg {String} msgFx @hide
27631      */
27632     /**
27633      * @cfg {String} validateOnBlur @hide
27634      */
27635 });
27636
27637 Roo.HtmlEditorCore.white = [
27638         'area', 'br', 'img', 'input', 'hr', 'wbr',
27639         
27640        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27641        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27642        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27643        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27644        'table',   'ul',         'xmp', 
27645        
27646        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27647       'thead',   'tr', 
27648      
27649       'dir', 'menu', 'ol', 'ul', 'dl',
27650        
27651       'embed',  'object'
27652 ];
27653
27654
27655 Roo.HtmlEditorCore.black = [
27656     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27657         'applet', // 
27658         'base',   'basefont', 'bgsound', 'blink',  'body', 
27659         'frame',  'frameset', 'head',    'html',   'ilayer', 
27660         'iframe', 'layer',  'link',     'meta',    'object',   
27661         'script', 'style' ,'title',  'xml' // clean later..
27662 ];
27663 Roo.HtmlEditorCore.clean = [
27664     'script', 'style', 'title', 'xml'
27665 ];
27666 Roo.HtmlEditorCore.remove = [
27667     'font'
27668 ];
27669 // attributes..
27670
27671 Roo.HtmlEditorCore.ablack = [
27672     'on'
27673 ];
27674     
27675 Roo.HtmlEditorCore.aclean = [ 
27676     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27677 ];
27678
27679 // protocols..
27680 Roo.HtmlEditorCore.pwhite= [
27681         'http',  'https',  'mailto'
27682 ];
27683
27684 // white listed style attributes.
27685 Roo.HtmlEditorCore.cwhite= [
27686       //  'text-align', /// default is to allow most things..
27687       
27688          
27689 //        'font-size'//??
27690 ];
27691
27692 // black listed style attributes.
27693 Roo.HtmlEditorCore.cblack= [
27694       //  'font-size' -- this can be set by the project 
27695 ];
27696
27697
27698 Roo.HtmlEditorCore.swapCodes   =[ 
27699     [    8211, "&#8211;" ], 
27700     [    8212, "&#8212;" ], 
27701     [    8216,  "'" ],  
27702     [    8217, "'" ],  
27703     [    8220, '"' ],  
27704     [    8221, '"' ],  
27705     [    8226, "*" ],  
27706     [    8230, "..." ]
27707 ]; 
27708
27709     /*
27710  * - LGPL
27711  *
27712  * HtmlEditor
27713  * 
27714  */
27715
27716 /**
27717  * @class Roo.bootstrap.form.HtmlEditor
27718  * @extends Roo.bootstrap.form.TextArea
27719  * Bootstrap HtmlEditor class
27720
27721  * @constructor
27722  * Create a new HtmlEditor
27723  * @param {Object} config The config object
27724  */
27725
27726 Roo.bootstrap.form.HtmlEditor = function(config){
27727     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27728     if (!this.toolbars) {
27729         this.toolbars = [];
27730     }
27731     
27732     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27733     this.addEvents({
27734             /**
27735              * @event initialize
27736              * Fires when the editor is fully initialized (including the iframe)
27737              * @param {HtmlEditor} this
27738              */
27739             initialize: true,
27740             /**
27741              * @event activate
27742              * Fires when the editor is first receives the focus. Any insertion must wait
27743              * until after this event.
27744              * @param {HtmlEditor} this
27745              */
27746             activate: true,
27747              /**
27748              * @event beforesync
27749              * Fires before the textarea is updated with content from the editor iframe. Return false
27750              * to cancel the sync.
27751              * @param {HtmlEditor} this
27752              * @param {String} html
27753              */
27754             beforesync: true,
27755              /**
27756              * @event beforepush
27757              * Fires before the iframe editor is updated with content from the textarea. Return false
27758              * to cancel the push.
27759              * @param {HtmlEditor} this
27760              * @param {String} html
27761              */
27762             beforepush: true,
27763              /**
27764              * @event sync
27765              * Fires when the textarea is updated with content from the editor iframe.
27766              * @param {HtmlEditor} this
27767              * @param {String} html
27768              */
27769             sync: true,
27770              /**
27771              * @event push
27772              * Fires when the iframe editor is updated with content from the textarea.
27773              * @param {HtmlEditor} this
27774              * @param {String} html
27775              */
27776             push: true,
27777              /**
27778              * @event editmodechange
27779              * Fires when the editor switches edit modes
27780              * @param {HtmlEditor} this
27781              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27782              */
27783             editmodechange: true,
27784             /**
27785              * @event editorevent
27786              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27787              * @param {HtmlEditor} this
27788              */
27789             editorevent: true,
27790             /**
27791              * @event firstfocus
27792              * Fires when on first focus - needed by toolbars..
27793              * @param {HtmlEditor} this
27794              */
27795             firstfocus: true,
27796             /**
27797              * @event autosave
27798              * Auto save the htmlEditor value as a file into Events
27799              * @param {HtmlEditor} this
27800              */
27801             autosave: true,
27802             /**
27803              * @event savedpreview
27804              * preview the saved version of htmlEditor
27805              * @param {HtmlEditor} this
27806              */
27807             savedpreview: true
27808         });
27809 };
27810
27811
27812 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
27813     
27814     
27815       /**
27816      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27817      */
27818     toolbars : false,
27819     
27820      /**
27821     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27822     */
27823     btns : [],
27824    
27825      /**
27826      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27827      *                        Roo.resizable.
27828      */
27829     resizable : false,
27830      /**
27831      * @cfg {Number} height (in pixels)
27832      */   
27833     height: 300,
27834    /**
27835      * @cfg {Number} width (in pixels)
27836      */   
27837     width: false,
27838     
27839     /**
27840      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27841      * 
27842      */
27843     stylesheets: false,
27844     
27845     // id of frame..
27846     frameId: false,
27847     
27848     // private properties
27849     validationEvent : false,
27850     deferHeight: true,
27851     initialized : false,
27852     activated : false,
27853     
27854     onFocus : Roo.emptyFn,
27855     iframePad:3,
27856     hideMode:'offsets',
27857     
27858     tbContainer : false,
27859     
27860     bodyCls : '',
27861     
27862     toolbarContainer :function() {
27863         return this.wrap.select('.x-html-editor-tb',true).first();
27864     },
27865
27866     /**
27867      * Protected method that will not generally be called directly. It
27868      * is called when the editor creates its toolbar. Override this method if you need to
27869      * add custom toolbar buttons.
27870      * @param {HtmlEditor} editor
27871      */
27872     createToolbar : function(){
27873         Roo.log('renewing');
27874         Roo.log("create toolbars");
27875         
27876         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27877         this.toolbars[0].render(this.toolbarContainer());
27878         
27879         return;
27880         
27881 //        if (!editor.toolbars || !editor.toolbars.length) {
27882 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27883 //        }
27884 //        
27885 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27886 //            editor.toolbars[i] = Roo.factory(
27887 //                    typeof(editor.toolbars[i]) == 'string' ?
27888 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27889 //                Roo.bootstrap.form.HtmlEditor);
27890 //            editor.toolbars[i].init(editor);
27891 //        }
27892     },
27893
27894      
27895     // private
27896     onRender : function(ct, position)
27897     {
27898        // Roo.log("Call onRender: " + this.xtype);
27899         var _t = this;
27900         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27901       
27902         this.wrap = this.inputEl().wrap({
27903             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27904         });
27905         
27906         this.editorcore.onRender(ct, position);
27907          
27908         if (this.resizable) {
27909             this.resizeEl = new Roo.Resizable(this.wrap, {
27910                 pinned : true,
27911                 wrap: true,
27912                 dynamic : true,
27913                 minHeight : this.height,
27914                 height: this.height,
27915                 handles : this.resizable,
27916                 width: this.width,
27917                 listeners : {
27918                     resize : function(r, w, h) {
27919                         _t.onResize(w,h); // -something
27920                     }
27921                 }
27922             });
27923             
27924         }
27925         this.createToolbar(this);
27926        
27927         
27928         if(!this.width && this.resizable){
27929             this.setSize(this.wrap.getSize());
27930         }
27931         if (this.resizeEl) {
27932             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27933             // should trigger onReize..
27934         }
27935         
27936     },
27937
27938     // private
27939     onResize : function(w, h)
27940     {
27941         Roo.log('resize: ' +w + ',' + h );
27942         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27943         var ew = false;
27944         var eh = false;
27945         
27946         if(this.inputEl() ){
27947             if(typeof w == 'number'){
27948                 var aw = w - this.wrap.getFrameWidth('lr');
27949                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27950                 ew = aw;
27951             }
27952             if(typeof h == 'number'){
27953                  var tbh = -11;  // fixme it needs to tool bar size!
27954                 for (var i =0; i < this.toolbars.length;i++) {
27955                     // fixme - ask toolbars for heights?
27956                     tbh += this.toolbars[i].el.getHeight();
27957                     //if (this.toolbars[i].footer) {
27958                     //    tbh += this.toolbars[i].footer.el.getHeight();
27959                     //}
27960                 }
27961               
27962                 
27963                 
27964                 
27965                 
27966                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27967                 ah -= 5; // knock a few pixes off for look..
27968                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27969                 var eh = ah;
27970             }
27971         }
27972         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27973         this.editorcore.onResize(ew,eh);
27974         
27975     },
27976
27977     /**
27978      * Toggles the editor between standard and source edit mode.
27979      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27980      */
27981     toggleSourceEdit : function(sourceEditMode)
27982     {
27983         this.editorcore.toggleSourceEdit(sourceEditMode);
27984         
27985         if(this.editorcore.sourceEditMode){
27986             Roo.log('editor - showing textarea');
27987             
27988 //            Roo.log('in');
27989 //            Roo.log(this.syncValue());
27990             this.syncValue();
27991             this.inputEl().removeClass(['hide', 'x-hidden']);
27992             this.inputEl().dom.removeAttribute('tabIndex');
27993             this.inputEl().focus();
27994         }else{
27995             Roo.log('editor - hiding textarea');
27996 //            Roo.log('out')
27997 //            Roo.log(this.pushValue()); 
27998             this.pushValue();
27999             
28000             this.inputEl().addClass(['hide', 'x-hidden']);
28001             this.inputEl().dom.setAttribute('tabIndex', -1);
28002             //this.deferFocus();
28003         }
28004          
28005         if(this.resizable){
28006             this.setSize(this.wrap.getSize());
28007         }
28008         
28009         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
28010     },
28011  
28012     // private (for BoxComponent)
28013     adjustSize : Roo.BoxComponent.prototype.adjustSize,
28014
28015     // private (for BoxComponent)
28016     getResizeEl : function(){
28017         return this.wrap;
28018     },
28019
28020     // private (for BoxComponent)
28021     getPositionEl : function(){
28022         return this.wrap;
28023     },
28024
28025     // private
28026     initEvents : function(){
28027         this.originalValue = this.getValue();
28028     },
28029
28030 //    /**
28031 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28032 //     * @method
28033 //     */
28034 //    markInvalid : Roo.emptyFn,
28035 //    /**
28036 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28037 //     * @method
28038 //     */
28039 //    clearInvalid : Roo.emptyFn,
28040
28041     setValue : function(v){
28042         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28043         this.editorcore.pushValue();
28044     },
28045
28046      
28047     // private
28048     deferFocus : function(){
28049         this.focus.defer(10, this);
28050     },
28051
28052     // doc'ed in Field
28053     focus : function(){
28054         this.editorcore.focus();
28055         
28056     },
28057       
28058
28059     // private
28060     onDestroy : function(){
28061         
28062         
28063         
28064         if(this.rendered){
28065             
28066             for (var i =0; i < this.toolbars.length;i++) {
28067                 // fixme - ask toolbars for heights?
28068                 this.toolbars[i].onDestroy();
28069             }
28070             
28071             this.wrap.dom.innerHTML = '';
28072             this.wrap.remove();
28073         }
28074     },
28075
28076     // private
28077     onFirstFocus : function(){
28078         //Roo.log("onFirstFocus");
28079         this.editorcore.onFirstFocus();
28080          for (var i =0; i < this.toolbars.length;i++) {
28081             this.toolbars[i].onFirstFocus();
28082         }
28083         
28084     },
28085     
28086     // private
28087     syncValue : function()
28088     {   
28089         this.editorcore.syncValue();
28090     },
28091     
28092     pushValue : function()
28093     {   
28094         this.editorcore.pushValue();
28095     }
28096      
28097     
28098     // hide stuff that is not compatible
28099     /**
28100      * @event blur
28101      * @hide
28102      */
28103     /**
28104      * @event change
28105      * @hide
28106      */
28107     /**
28108      * @event focus
28109      * @hide
28110      */
28111     /**
28112      * @event specialkey
28113      * @hide
28114      */
28115     /**
28116      * @cfg {String} fieldClass @hide
28117      */
28118     /**
28119      * @cfg {String} focusClass @hide
28120      */
28121     /**
28122      * @cfg {String} autoCreate @hide
28123      */
28124     /**
28125      * @cfg {String} inputType @hide
28126      */
28127      
28128     /**
28129      * @cfg {String} invalidText @hide
28130      */
28131     /**
28132      * @cfg {String} msgFx @hide
28133      */
28134     /**
28135      * @cfg {String} validateOnBlur @hide
28136      */
28137 });
28138  
28139     
28140    
28141    
28142    
28143       
28144 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28145 /**
28146  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28147  * @parent Roo.bootstrap.form.HtmlEditor
28148  * @extends Roo.bootstrap.nav.Simplebar
28149  * Basic Toolbar
28150  * 
28151  * @example
28152  * Usage:
28153  *
28154  new Roo.bootstrap.form.HtmlEditor({
28155     ....
28156     toolbars : [
28157         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28158             disable : { fonts: 1 , format: 1, ..., ... , ...],
28159             btns : [ .... ]
28160         })
28161     }
28162      
28163  * 
28164  * @cfg {Object} disable List of elements to disable..
28165  * @cfg {Array} btns List of additional buttons.
28166  * 
28167  * 
28168  * NEEDS Extra CSS? 
28169  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28170  */
28171  
28172 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28173 {
28174     
28175     Roo.apply(this, config);
28176     
28177     // default disabled, based on 'good practice'..
28178     this.disable = this.disable || {};
28179     Roo.applyIf(this.disable, {
28180         fontSize : true,
28181         colors : true,
28182         specialElements : true
28183     });
28184     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28185     
28186     this.editor = config.editor;
28187     this.editorcore = config.editor.editorcore;
28188     
28189     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28190     
28191     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28192     // dont call parent... till later.
28193 }
28194 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
28195      
28196     bar : true,
28197     
28198     editor : false,
28199     editorcore : false,
28200     
28201     
28202     formats : [
28203         "p" ,  
28204         "h1","h2","h3","h4","h5","h6", 
28205         "pre", "code", 
28206         "abbr", "acronym", "address", "cite", "samp", "var",
28207         'div','span'
28208     ],
28209     
28210     onRender : function(ct, position)
28211     {
28212        // Roo.log("Call onRender: " + this.xtype);
28213         
28214        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28215        Roo.log(this.el);
28216        this.el.dom.style.marginBottom = '0';
28217        var _this = this;
28218        var editorcore = this.editorcore;
28219        var editor= this.editor;
28220        
28221        var children = [];
28222        var btn = function(id,cmd , toggle, handler, html){
28223        
28224             var  event = toggle ? 'toggle' : 'click';
28225        
28226             var a = {
28227                 size : 'sm',
28228                 xtype: 'Button',
28229                 xns: Roo.bootstrap,
28230                 //glyphicon : id,
28231                 fa: id,
28232                 cmd : id || cmd,
28233                 enableToggle:toggle !== false,
28234                 html : html || '',
28235                 pressed : toggle ? false : null,
28236                 listeners : {}
28237             };
28238             a.listeners[toggle ? 'toggle' : 'click'] = function() {
28239                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
28240             };
28241             children.push(a);
28242             return a;
28243        }
28244        
28245     //    var cb_box = function...
28246         
28247         var style = {
28248                 xtype: 'Button',
28249                 size : 'sm',
28250                 xns: Roo.bootstrap,
28251                 fa : 'font',
28252                 //html : 'submit'
28253                 menu : {
28254                     xtype: 'Menu',
28255                     xns: Roo.bootstrap,
28256                     items:  []
28257                 }
28258         };
28259         Roo.each(this.formats, function(f) {
28260             style.menu.items.push({
28261                 xtype :'MenuItem',
28262                 xns: Roo.bootstrap,
28263                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28264                 tagname : f,
28265                 listeners : {
28266                     click : function()
28267                     {
28268                         editorcore.insertTag(this.tagname);
28269                         editor.focus();
28270                     }
28271                 }
28272                 
28273             });
28274         });
28275         children.push(style);   
28276         
28277         btn('bold',false,true);
28278         btn('italic',false,true);
28279         btn('align-left', 'justifyleft',true);
28280         btn('align-center', 'justifycenter',true);
28281         btn('align-right' , 'justifyright',true);
28282         btn('link', false, false, function(btn) {
28283             //Roo.log("create link?");
28284             var url = prompt(this.createLinkText, this.defaultLinkValue);
28285             if(url && url != 'http:/'+'/'){
28286                 this.editorcore.relayCmd('createlink', url);
28287             }
28288         }),
28289         btn('list','insertunorderedlist',true);
28290         btn('pencil', false,true, function(btn){
28291                 Roo.log(this);
28292                 this.toggleSourceEdit(btn.pressed);
28293         });
28294         
28295         if (this.editor.btns.length > 0) {
28296             for (var i = 0; i<this.editor.btns.length; i++) {
28297                 children.push(this.editor.btns[i]);
28298             }
28299         }
28300         
28301         /*
28302         var cog = {
28303                 xtype: 'Button',
28304                 size : 'sm',
28305                 xns: Roo.bootstrap,
28306                 glyphicon : 'cog',
28307                 //html : 'submit'
28308                 menu : {
28309                     xtype: 'Menu',
28310                     xns: Roo.bootstrap,
28311                     items:  []
28312                 }
28313         };
28314         
28315         cog.menu.items.push({
28316             xtype :'MenuItem',
28317             xns: Roo.bootstrap,
28318             html : Clean styles,
28319             tagname : f,
28320             listeners : {
28321                 click : function()
28322                 {
28323                     editorcore.insertTag(this.tagname);
28324                     editor.focus();
28325                 }
28326             }
28327             
28328         });
28329        */
28330         
28331          
28332        this.xtype = 'NavSimplebar';
28333         
28334         for(var i=0;i< children.length;i++) {
28335             
28336             this.buttons.add(this.addxtypeChild(children[i]));
28337             
28338         }
28339         
28340         editor.on('editorevent', this.updateToolbar, this);
28341     },
28342     onBtnClick : function(id)
28343     {
28344        this.editorcore.relayCmd(id);
28345        this.editorcore.focus();
28346     },
28347     
28348     /**
28349      * Protected method that will not generally be called directly. It triggers
28350      * a toolbar update by reading the markup state of the current selection in the editor.
28351      */
28352     updateToolbar: function(){
28353
28354         if(!this.editorcore.activated){
28355             this.editor.onFirstFocus(); // is this neeed?
28356             return;
28357         }
28358
28359         var btns = this.buttons; 
28360         var doc = this.editorcore.doc;
28361         btns.get('bold').setActive(doc.queryCommandState('bold'));
28362         btns.get('italic').setActive(doc.queryCommandState('italic'));
28363         //btns.get('underline').setActive(doc.queryCommandState('underline'));
28364         
28365         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28366         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28367         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28368         
28369         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28370         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28371          /*
28372         
28373         var ans = this.editorcore.getAllAncestors();
28374         if (this.formatCombo) {
28375             
28376             
28377             var store = this.formatCombo.store;
28378             this.formatCombo.setValue("");
28379             for (var i =0; i < ans.length;i++) {
28380                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28381                     // select it..
28382                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28383                     break;
28384                 }
28385             }
28386         }
28387         
28388         
28389         
28390         // hides menus... - so this cant be on a menu...
28391         Roo.bootstrap.MenuMgr.hideAll();
28392         */
28393         Roo.bootstrap.menu.Manager.hideAll();
28394         //this.editorsyncValue();
28395     },
28396     onFirstFocus: function() {
28397         this.buttons.each(function(item){
28398            item.enable();
28399         });
28400     },
28401     toggleSourceEdit : function(sourceEditMode){
28402         
28403           
28404         if(sourceEditMode){
28405             Roo.log("disabling buttons");
28406            this.buttons.each( function(item){
28407                 if(item.cmd != 'pencil'){
28408                     item.disable();
28409                 }
28410             });
28411           
28412         }else{
28413             Roo.log("enabling buttons");
28414             if(this.editorcore.initialized){
28415                 this.buttons.each( function(item){
28416                     item.enable();
28417                 });
28418             }
28419             
28420         }
28421         Roo.log("calling toggole on editor");
28422         // tell the editor that it's been pressed..
28423         this.editor.toggleSourceEdit(sourceEditMode);
28424        
28425     }
28426 });
28427
28428
28429
28430
28431  
28432 /*
28433  * - LGPL
28434  */
28435
28436 /**
28437  * @class Roo.bootstrap.form.Markdown
28438  * @extends Roo.bootstrap.form.TextArea
28439  * Bootstrap Showdown editable area
28440  * @cfg {string} content
28441  * 
28442  * @constructor
28443  * Create a new Showdown
28444  */
28445
28446 Roo.bootstrap.form.Markdown = function(config){
28447     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28448    
28449 };
28450
28451 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
28452     
28453     editing :false,
28454     
28455     initEvents : function()
28456     {
28457         
28458         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28459         this.markdownEl = this.el.createChild({
28460             cls : 'roo-markdown-area'
28461         });
28462         this.inputEl().addClass('d-none');
28463         if (this.getValue() == '') {
28464             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28465             
28466         } else {
28467             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28468         }
28469         this.markdownEl.on('click', this.toggleTextEdit, this);
28470         this.on('blur', this.toggleTextEdit, this);
28471         this.on('specialkey', this.resizeTextArea, this);
28472     },
28473     
28474     toggleTextEdit : function()
28475     {
28476         var sh = this.markdownEl.getHeight();
28477         this.inputEl().addClass('d-none');
28478         this.markdownEl.addClass('d-none');
28479         if (!this.editing) {
28480             // show editor?
28481             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28482             this.inputEl().removeClass('d-none');
28483             this.inputEl().focus();
28484             this.editing = true;
28485             return;
28486         }
28487         // show showdown...
28488         this.updateMarkdown();
28489         this.markdownEl.removeClass('d-none');
28490         this.editing = false;
28491         return;
28492     },
28493     updateMarkdown : function()
28494     {
28495         if (this.getValue() == '') {
28496             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28497             return;
28498         }
28499  
28500         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28501     },
28502     
28503     resizeTextArea: function () {
28504         
28505         var sh = 100;
28506         Roo.log([sh, this.getValue().split("\n").length * 30]);
28507         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28508     },
28509     setValue : function(val)
28510     {
28511         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28512         if (!this.editing) {
28513             this.updateMarkdown();
28514         }
28515         
28516     },
28517     focus : function()
28518     {
28519         if (!this.editing) {
28520             this.toggleTextEdit();
28521         }
28522         
28523     }
28524
28525
28526 });/*
28527  * Based on:
28528  * Ext JS Library 1.1.1
28529  * Copyright(c) 2006-2007, Ext JS, LLC.
28530  *
28531  * Originally Released Under LGPL - original licence link has changed is not relivant.
28532  *
28533  * Fork - LGPL
28534  * <script type="text/javascript">
28535  */
28536  
28537 /**
28538  * @class Roo.bootstrap.PagingToolbar
28539  * @extends Roo.bootstrap.nav.Simplebar
28540  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28541  * @constructor
28542  * Create a new PagingToolbar
28543  * @param {Object} config The config object
28544  * @param {Roo.data.Store} store
28545  */
28546 Roo.bootstrap.PagingToolbar = function(config)
28547 {
28548     // old args format still supported... - xtype is prefered..
28549         // created from xtype...
28550     
28551     this.ds = config.dataSource;
28552     
28553     if (config.store && !this.ds) {
28554         this.store= Roo.factory(config.store, Roo.data);
28555         this.ds = this.store;
28556         this.ds.xmodule = this.xmodule || false;
28557     }
28558     
28559     this.toolbarItems = [];
28560     if (config.items) {
28561         this.toolbarItems = config.items;
28562     }
28563     
28564     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28565     
28566     this.cursor = 0;
28567     
28568     if (this.ds) { 
28569         this.bind(this.ds);
28570     }
28571     
28572     if (Roo.bootstrap.version == 4) {
28573         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28574     } else {
28575         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28576     }
28577     
28578 };
28579
28580 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28581     /**
28582      * @cfg {Roo.bootstrap.Button} buttons[]
28583      * Buttons for the toolbar
28584      */
28585      /**
28586      * @cfg {Roo.data.Store} store
28587      * The underlying data store providing the paged data
28588      */
28589     /**
28590      * @cfg {String/HTMLElement/Element} container
28591      * container The id or element that will contain the toolbar
28592      */
28593     /**
28594      * @cfg {Boolean} displayInfo
28595      * True to display the displayMsg (defaults to false)
28596      */
28597     /**
28598      * @cfg {Number} pageSize
28599      * The number of records to display per page (defaults to 20)
28600      */
28601     pageSize: 20,
28602     /**
28603      * @cfg {String} displayMsg
28604      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28605      */
28606     displayMsg : 'Displaying {0} - {1} of {2}',
28607     /**
28608      * @cfg {String} emptyMsg
28609      * The message to display when no records are found (defaults to "No data to display")
28610      */
28611     emptyMsg : 'No data to display',
28612     /**
28613      * Customizable piece of the default paging text (defaults to "Page")
28614      * @type String
28615      */
28616     beforePageText : "Page",
28617     /**
28618      * Customizable piece of the default paging text (defaults to "of %0")
28619      * @type String
28620      */
28621     afterPageText : "of {0}",
28622     /**
28623      * Customizable piece of the default paging text (defaults to "First Page")
28624      * @type String
28625      */
28626     firstText : "First Page",
28627     /**
28628      * Customizable piece of the default paging text (defaults to "Previous Page")
28629      * @type String
28630      */
28631     prevText : "Previous Page",
28632     /**
28633      * Customizable piece of the default paging text (defaults to "Next Page")
28634      * @type String
28635      */
28636     nextText : "Next Page",
28637     /**
28638      * Customizable piece of the default paging text (defaults to "Last Page")
28639      * @type String
28640      */
28641     lastText : "Last Page",
28642     /**
28643      * Customizable piece of the default paging text (defaults to "Refresh")
28644      * @type String
28645      */
28646     refreshText : "Refresh",
28647
28648     buttons : false,
28649     // private
28650     onRender : function(ct, position) 
28651     {
28652         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28653         this.navgroup.parentId = this.id;
28654         this.navgroup.onRender(this.el, null);
28655         // add the buttons to the navgroup
28656         
28657         if(this.displayInfo){
28658             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28659             this.displayEl = this.el.select('.x-paging-info', true).first();
28660 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28661 //            this.displayEl = navel.el.select('span',true).first();
28662         }
28663         
28664         var _this = this;
28665         
28666         if(this.buttons){
28667             Roo.each(_this.buttons, function(e){ // this might need to use render????
28668                Roo.factory(e).render(_this.el);
28669             });
28670         }
28671             
28672         Roo.each(_this.toolbarItems, function(e) {
28673             _this.navgroup.addItem(e);
28674         });
28675         
28676         
28677         this.first = this.navgroup.addItem({
28678             tooltip: this.firstText,
28679             cls: "prev btn-outline-secondary",
28680             html : ' <i class="fa fa-step-backward"></i>',
28681             disabled: true,
28682             preventDefault: true,
28683             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28684         });
28685         
28686         this.prev =  this.navgroup.addItem({
28687             tooltip: this.prevText,
28688             cls: "prev btn-outline-secondary",
28689             html : ' <i class="fa fa-backward"></i>',
28690             disabled: true,
28691             preventDefault: true,
28692             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28693         });
28694     //this.addSeparator();
28695         
28696         
28697         var field = this.navgroup.addItem( {
28698             tagtype : 'span',
28699             cls : 'x-paging-position  btn-outline-secondary',
28700              disabled: true,
28701             html : this.beforePageText  +
28702                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28703                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28704          } ); //?? escaped?
28705         
28706         this.field = field.el.select('input', true).first();
28707         this.field.on("keydown", this.onPagingKeydown, this);
28708         this.field.on("focus", function(){this.dom.select();});
28709     
28710     
28711         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28712         //this.field.setHeight(18);
28713         //this.addSeparator();
28714         this.next = this.navgroup.addItem({
28715             tooltip: this.nextText,
28716             cls: "next btn-outline-secondary",
28717             html : ' <i class="fa fa-forward"></i>',
28718             disabled: true,
28719             preventDefault: true,
28720             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28721         });
28722         this.last = this.navgroup.addItem({
28723             tooltip: this.lastText,
28724             html : ' <i class="fa fa-step-forward"></i>',
28725             cls: "next btn-outline-secondary",
28726             disabled: true,
28727             preventDefault: true,
28728             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28729         });
28730     //this.addSeparator();
28731         this.loading = this.navgroup.addItem({
28732             tooltip: this.refreshText,
28733             cls: "btn-outline-secondary",
28734             html : ' <i class="fa fa-refresh"></i>',
28735             preventDefault: true,
28736             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28737         });
28738         
28739     },
28740
28741     // private
28742     updateInfo : function(){
28743         if(this.displayEl){
28744             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28745             var msg = count == 0 ?
28746                 this.emptyMsg :
28747                 String.format(
28748                     this.displayMsg,
28749                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28750                 );
28751             this.displayEl.update(msg);
28752         }
28753     },
28754
28755     // private
28756     onLoad : function(ds, r, o)
28757     {
28758         this.cursor = o.params && o.params.start ? o.params.start : 0;
28759         
28760         var d = this.getPageData(),
28761             ap = d.activePage,
28762             ps = d.pages;
28763         
28764         
28765         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28766         this.field.dom.value = ap;
28767         this.first.setDisabled(ap == 1);
28768         this.prev.setDisabled(ap == 1);
28769         this.next.setDisabled(ap == ps);
28770         this.last.setDisabled(ap == ps);
28771         this.loading.enable();
28772         this.updateInfo();
28773     },
28774
28775     // private
28776     getPageData : function(){
28777         var total = this.ds.getTotalCount();
28778         return {
28779             total : total,
28780             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28781             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28782         };
28783     },
28784
28785     // private
28786     onLoadError : function(){
28787         this.loading.enable();
28788     },
28789
28790     // private
28791     onPagingKeydown : function(e){
28792         var k = e.getKey();
28793         var d = this.getPageData();
28794         if(k == e.RETURN){
28795             var v = this.field.dom.value, pageNum;
28796             if(!v || isNaN(pageNum = parseInt(v, 10))){
28797                 this.field.dom.value = d.activePage;
28798                 return;
28799             }
28800             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28801             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28802             e.stopEvent();
28803         }
28804         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))
28805         {
28806           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28807           this.field.dom.value = pageNum;
28808           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28809           e.stopEvent();
28810         }
28811         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28812         {
28813           var v = this.field.dom.value, pageNum; 
28814           var increment = (e.shiftKey) ? 10 : 1;
28815           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28816                 increment *= -1;
28817           }
28818           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28819             this.field.dom.value = d.activePage;
28820             return;
28821           }
28822           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28823           {
28824             this.field.dom.value = parseInt(v, 10) + increment;
28825             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28826             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28827           }
28828           e.stopEvent();
28829         }
28830     },
28831
28832     // private
28833     beforeLoad : function(){
28834         if(this.loading){
28835             this.loading.disable();
28836         }
28837     },
28838
28839     // private
28840     onClick : function(which){
28841         
28842         var ds = this.ds;
28843         if (!ds) {
28844             return;
28845         }
28846         
28847         switch(which){
28848             case "first":
28849                 ds.load({params:{start: 0, limit: this.pageSize}});
28850             break;
28851             case "prev":
28852                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28853             break;
28854             case "next":
28855                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28856             break;
28857             case "last":
28858                 var total = ds.getTotalCount();
28859                 var extra = total % this.pageSize;
28860                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28861                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28862             break;
28863             case "refresh":
28864                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28865             break;
28866         }
28867     },
28868
28869     /**
28870      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28871      * @param {Roo.data.Store} store The data store to unbind
28872      */
28873     unbind : function(ds){
28874         ds.un("beforeload", this.beforeLoad, this);
28875         ds.un("load", this.onLoad, this);
28876         ds.un("loadexception", this.onLoadError, this);
28877         ds.un("remove", this.updateInfo, this);
28878         ds.un("add", this.updateInfo, this);
28879         this.ds = undefined;
28880     },
28881
28882     /**
28883      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28884      * @param {Roo.data.Store} store The data store to bind
28885      */
28886     bind : function(ds){
28887         ds.on("beforeload", this.beforeLoad, this);
28888         ds.on("load", this.onLoad, this);
28889         ds.on("loadexception", this.onLoadError, this);
28890         ds.on("remove", this.updateInfo, this);
28891         ds.on("add", this.updateInfo, this);
28892         this.ds = ds;
28893     }
28894 });/*
28895  * - LGPL
28896  *
28897  * element
28898  * 
28899  */
28900
28901 /**
28902  * @class Roo.bootstrap.MessageBar
28903  * @extends Roo.bootstrap.Component
28904  * Bootstrap MessageBar class
28905  * @cfg {String} html contents of the MessageBar
28906  * @cfg {String} weight (info | success | warning | danger) default info
28907  * @cfg {String} beforeClass insert the bar before the given class
28908  * @cfg {Boolean} closable (true | false) default false
28909  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28910  * 
28911  * @constructor
28912  * Create a new Element
28913  * @param {Object} config The config object
28914  */
28915
28916 Roo.bootstrap.MessageBar = function(config){
28917     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28918 };
28919
28920 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28921     
28922     html: '',
28923     weight: 'info',
28924     closable: false,
28925     fixed: false,
28926     beforeClass: 'bootstrap-sticky-wrap',
28927     
28928     getAutoCreate : function(){
28929         
28930         var cfg = {
28931             tag: 'div',
28932             cls: 'alert alert-dismissable alert-' + this.weight,
28933             cn: [
28934                 {
28935                     tag: 'span',
28936                     cls: 'message',
28937                     html: this.html || ''
28938                 }
28939             ]
28940         };
28941         
28942         if(this.fixed){
28943             cfg.cls += ' alert-messages-fixed';
28944         }
28945         
28946         if(this.closable){
28947             cfg.cn.push({
28948                 tag: 'button',
28949                 cls: 'close',
28950                 html: 'x'
28951             });
28952         }
28953         
28954         return cfg;
28955     },
28956     
28957     onRender : function(ct, position)
28958     {
28959         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28960         
28961         if(!this.el){
28962             var cfg = Roo.apply({},  this.getAutoCreate());
28963             cfg.id = Roo.id();
28964             
28965             if (this.cls) {
28966                 cfg.cls += ' ' + this.cls;
28967             }
28968             if (this.style) {
28969                 cfg.style = this.style;
28970             }
28971             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28972             
28973             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28974         }
28975         
28976         this.el.select('>button.close').on('click', this.hide, this);
28977         
28978     },
28979     
28980     show : function()
28981     {
28982         if (!this.rendered) {
28983             this.render();
28984         }
28985         
28986         this.el.show();
28987         
28988         this.fireEvent('show', this);
28989         
28990     },
28991     
28992     hide : function()
28993     {
28994         if (!this.rendered) {
28995             this.render();
28996         }
28997         
28998         this.el.hide();
28999         
29000         this.fireEvent('hide', this);
29001     },
29002     
29003     update : function()
29004     {
29005 //        var e = this.el.dom.firstChild;
29006 //        
29007 //        if(this.closable){
29008 //            e = e.nextSibling;
29009 //        }
29010 //        
29011 //        e.data = this.html || '';
29012
29013         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29014     }
29015    
29016 });
29017
29018  
29019
29020      /*
29021  * - LGPL
29022  *
29023  * Graph
29024  * 
29025  */
29026
29027
29028 /**
29029  * @class Roo.bootstrap.Graph
29030  * @extends Roo.bootstrap.Component
29031  * Bootstrap Graph class
29032 > Prameters
29033  -sm {number} sm 4
29034  -md {number} md 5
29035  @cfg {String} graphtype  bar | vbar | pie
29036  @cfg {number} g_x coodinator | centre x (pie)
29037  @cfg {number} g_y coodinator | centre y (pie)
29038  @cfg {number} g_r radius (pie)
29039  @cfg {number} g_height height of the chart (respected by all elements in the set)
29040  @cfg {number} g_width width of the chart (respected by all elements in the set)
29041  @cfg {Object} title The title of the chart
29042     
29043  -{Array}  values
29044  -opts (object) options for the chart 
29045      o {
29046      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29047      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29048      o vgutter (number)
29049      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.
29050      o stacked (boolean) whether or not to tread values as in a stacked bar chart
29051      o to
29052      o stretch (boolean)
29053      o }
29054  -opts (object) options for the pie
29055      o{
29056      o cut
29057      o startAngle (number)
29058      o endAngle (number)
29059      } 
29060  *
29061  * @constructor
29062  * Create a new Input
29063  * @param {Object} config The config object
29064  */
29065
29066 Roo.bootstrap.Graph = function(config){
29067     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29068     
29069     this.addEvents({
29070         // img events
29071         /**
29072          * @event click
29073          * The img click event for the img.
29074          * @param {Roo.EventObject} e
29075          */
29076         "click" : true
29077     });
29078 };
29079
29080 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
29081     
29082     sm: 4,
29083     md: 5,
29084     graphtype: 'bar',
29085     g_height: 250,
29086     g_width: 400,
29087     g_x: 50,
29088     g_y: 50,
29089     g_r: 30,
29090     opts:{
29091         //g_colors: this.colors,
29092         g_type: 'soft',
29093         g_gutter: '20%'
29094
29095     },
29096     title : false,
29097
29098     getAutoCreate : function(){
29099         
29100         var cfg = {
29101             tag: 'div',
29102             html : null
29103         };
29104         
29105         
29106         return  cfg;
29107     },
29108
29109     onRender : function(ct,position){
29110         
29111         
29112         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29113         
29114         if (typeof(Raphael) == 'undefined') {
29115             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29116             return;
29117         }
29118         
29119         this.raphael = Raphael(this.el.dom);
29120         
29121                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29122                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29123                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29124                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29125                 /*
29126                 r.text(160, 10, "Single Series Chart").attr(txtattr);
29127                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29128                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29129                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29130                 
29131                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29132                 r.barchart(330, 10, 300, 220, data1);
29133                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29134                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29135                 */
29136                 
29137                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29138                 // r.barchart(30, 30, 560, 250,  xdata, {
29139                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29140                 //     axis : "0 0 1 1",
29141                 //     axisxlabels :  xdata
29142                 //     //yvalues : cols,
29143                    
29144                 // });
29145 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29146 //        
29147 //        this.load(null,xdata,{
29148 //                axis : "0 0 1 1",
29149 //                axisxlabels :  xdata
29150 //                });
29151
29152     },
29153
29154     load : function(graphtype,xdata,opts)
29155     {
29156         this.raphael.clear();
29157         if(!graphtype) {
29158             graphtype = this.graphtype;
29159         }
29160         if(!opts){
29161             opts = this.opts;
29162         }
29163         var r = this.raphael,
29164             fin = function () {
29165                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29166             },
29167             fout = function () {
29168                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29169             },
29170             pfin = function() {
29171                 this.sector.stop();
29172                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29173
29174                 if (this.label) {
29175                     this.label[0].stop();
29176                     this.label[0].attr({ r: 7.5 });
29177                     this.label[1].attr({ "font-weight": 800 });
29178                 }
29179             },
29180             pfout = function() {
29181                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29182
29183                 if (this.label) {
29184                     this.label[0].animate({ r: 5 }, 500, "bounce");
29185                     this.label[1].attr({ "font-weight": 400 });
29186                 }
29187             };
29188
29189         switch(graphtype){
29190             case 'bar':
29191                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29192                 break;
29193             case 'hbar':
29194                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29195                 break;
29196             case 'pie':
29197 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
29198 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29199 //            
29200                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29201                 
29202                 break;
29203
29204         }
29205         
29206         if(this.title){
29207             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29208         }
29209         
29210     },
29211     
29212     setTitle: function(o)
29213     {
29214         this.title = o;
29215     },
29216     
29217     initEvents: function() {
29218         
29219         if(!this.href){
29220             this.el.on('click', this.onClick, this);
29221         }
29222     },
29223     
29224     onClick : function(e)
29225     {
29226         Roo.log('img onclick');
29227         this.fireEvent('click', this, e);
29228     }
29229    
29230 });
29231
29232  
29233 /*
29234  * - LGPL
29235  *
29236  * numberBox
29237  * 
29238  */
29239 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29240
29241 /**
29242  * @class Roo.bootstrap.dash.NumberBox
29243  * @extends Roo.bootstrap.Component
29244  * Bootstrap NumberBox class
29245  * @cfg {String} headline Box headline
29246  * @cfg {String} content Box content
29247  * @cfg {String} icon Box icon
29248  * @cfg {String} footer Footer text
29249  * @cfg {String} fhref Footer href
29250  * 
29251  * @constructor
29252  * Create a new NumberBox
29253  * @param {Object} config The config object
29254  */
29255
29256
29257 Roo.bootstrap.dash.NumberBox = function(config){
29258     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29259     
29260 };
29261
29262 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
29263     
29264     headline : '',
29265     content : '',
29266     icon : '',
29267     footer : '',
29268     fhref : '',
29269     ficon : '',
29270     
29271     getAutoCreate : function(){
29272         
29273         var cfg = {
29274             tag : 'div',
29275             cls : 'small-box ',
29276             cn : [
29277                 {
29278                     tag : 'div',
29279                     cls : 'inner',
29280                     cn :[
29281                         {
29282                             tag : 'h3',
29283                             cls : 'roo-headline',
29284                             html : this.headline
29285                         },
29286                         {
29287                             tag : 'p',
29288                             cls : 'roo-content',
29289                             html : this.content
29290                         }
29291                     ]
29292                 }
29293             ]
29294         };
29295         
29296         if(this.icon){
29297             cfg.cn.push({
29298                 tag : 'div',
29299                 cls : 'icon',
29300                 cn :[
29301                     {
29302                         tag : 'i',
29303                         cls : 'ion ' + this.icon
29304                     }
29305                 ]
29306             });
29307         }
29308         
29309         if(this.footer){
29310             var footer = {
29311                 tag : 'a',
29312                 cls : 'small-box-footer',
29313                 href : this.fhref || '#',
29314                 html : this.footer
29315             };
29316             
29317             cfg.cn.push(footer);
29318             
29319         }
29320         
29321         return  cfg;
29322     },
29323
29324     onRender : function(ct,position){
29325         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29326
29327
29328        
29329                 
29330     },
29331
29332     setHeadline: function (value)
29333     {
29334         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29335     },
29336     
29337     setFooter: function (value, href)
29338     {
29339         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29340         
29341         if(href){
29342             this.el.select('a.small-box-footer',true).first().attr('href', href);
29343         }
29344         
29345     },
29346
29347     setContent: function (value)
29348     {
29349         this.el.select('.roo-content',true).first().dom.innerHTML = value;
29350     },
29351
29352     initEvents: function() 
29353     {   
29354         
29355     }
29356     
29357 });
29358
29359  
29360 /*
29361  * - LGPL
29362  *
29363  * TabBox
29364  * 
29365  */
29366 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29367
29368 /**
29369  * @class Roo.bootstrap.dash.TabBox
29370  * @extends Roo.bootstrap.Component
29371  * @children Roo.bootstrap.dash.TabPane
29372  * Bootstrap TabBox class
29373  * @cfg {String} title Title of the TabBox
29374  * @cfg {String} icon Icon of the TabBox
29375  * @cfg {Boolean} showtabs (true|false) show the tabs default true
29376  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29377  * 
29378  * @constructor
29379  * Create a new TabBox
29380  * @param {Object} config The config object
29381  */
29382
29383
29384 Roo.bootstrap.dash.TabBox = function(config){
29385     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29386     this.addEvents({
29387         // raw events
29388         /**
29389          * @event addpane
29390          * When a pane is added
29391          * @param {Roo.bootstrap.dash.TabPane} pane
29392          */
29393         "addpane" : true,
29394         /**
29395          * @event activatepane
29396          * When a pane is activated
29397          * @param {Roo.bootstrap.dash.TabPane} pane
29398          */
29399         "activatepane" : true
29400         
29401          
29402     });
29403     
29404     this.panes = [];
29405 };
29406
29407 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
29408
29409     title : '',
29410     icon : false,
29411     showtabs : true,
29412     tabScrollable : false,
29413     
29414     getChildContainer : function()
29415     {
29416         return this.el.select('.tab-content', true).first();
29417     },
29418     
29419     getAutoCreate : function(){
29420         
29421         var header = {
29422             tag: 'li',
29423             cls: 'pull-left header',
29424             html: this.title,
29425             cn : []
29426         };
29427         
29428         if(this.icon){
29429             header.cn.push({
29430                 tag: 'i',
29431                 cls: 'fa ' + this.icon
29432             });
29433         }
29434         
29435         var h = {
29436             tag: 'ul',
29437             cls: 'nav nav-tabs pull-right',
29438             cn: [
29439                 header
29440             ]
29441         };
29442         
29443         if(this.tabScrollable){
29444             h = {
29445                 tag: 'div',
29446                 cls: 'tab-header',
29447                 cn: [
29448                     {
29449                         tag: 'ul',
29450                         cls: 'nav nav-tabs pull-right',
29451                         cn: [
29452                             header
29453                         ]
29454                     }
29455                 ]
29456             };
29457         }
29458         
29459         var cfg = {
29460             tag: 'div',
29461             cls: 'nav-tabs-custom',
29462             cn: [
29463                 h,
29464                 {
29465                     tag: 'div',
29466                     cls: 'tab-content no-padding',
29467                     cn: []
29468                 }
29469             ]
29470         };
29471
29472         return  cfg;
29473     },
29474     initEvents : function()
29475     {
29476         //Roo.log('add add pane handler');
29477         this.on('addpane', this.onAddPane, this);
29478     },
29479      /**
29480      * Updates the box title
29481      * @param {String} html to set the title to.
29482      */
29483     setTitle : function(value)
29484     {
29485         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29486     },
29487     onAddPane : function(pane)
29488     {
29489         this.panes.push(pane);
29490         //Roo.log('addpane');
29491         //Roo.log(pane);
29492         // tabs are rendere left to right..
29493         if(!this.showtabs){
29494             return;
29495         }
29496         
29497         var ctr = this.el.select('.nav-tabs', true).first();
29498          
29499          
29500         var existing = ctr.select('.nav-tab',true);
29501         var qty = existing.getCount();;
29502         
29503         
29504         var tab = ctr.createChild({
29505             tag : 'li',
29506             cls : 'nav-tab' + (qty ? '' : ' active'),
29507             cn : [
29508                 {
29509                     tag : 'a',
29510                     href:'#',
29511                     html : pane.title
29512                 }
29513             ]
29514         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29515         pane.tab = tab;
29516         
29517         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29518         if (!qty) {
29519             pane.el.addClass('active');
29520         }
29521         
29522                 
29523     },
29524     onTabClick : function(ev,un,ob,pane)
29525     {
29526         //Roo.log('tab - prev default');
29527         ev.preventDefault();
29528         
29529         
29530         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29531         pane.tab.addClass('active');
29532         //Roo.log(pane.title);
29533         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29534         // technically we should have a deactivate event.. but maybe add later.
29535         // and it should not de-activate the selected tab...
29536         this.fireEvent('activatepane', pane);
29537         pane.el.addClass('active');
29538         pane.fireEvent('activate');
29539         
29540         
29541     },
29542     
29543     getActivePane : function()
29544     {
29545         var r = false;
29546         Roo.each(this.panes, function(p) {
29547             if(p.el.hasClass('active')){
29548                 r = p;
29549                 return false;
29550             }
29551             
29552             return;
29553         });
29554         
29555         return r;
29556     }
29557     
29558     
29559 });
29560
29561  
29562 /*
29563  * - LGPL
29564  *
29565  * Tab pane
29566  * 
29567  */
29568 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29569 /**
29570  * @class Roo.bootstrap.TabPane
29571  * @extends Roo.bootstrap.Component
29572  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
29573  * Bootstrap TabPane class
29574  * @cfg {Boolean} active (false | true) Default false
29575  * @cfg {String} title title of panel
29576
29577  * 
29578  * @constructor
29579  * Create a new TabPane
29580  * @param {Object} config The config object
29581  */
29582
29583 Roo.bootstrap.dash.TabPane = function(config){
29584     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29585     
29586     this.addEvents({
29587         // raw events
29588         /**
29589          * @event activate
29590          * When a pane is activated
29591          * @param {Roo.bootstrap.dash.TabPane} pane
29592          */
29593         "activate" : true
29594          
29595     });
29596 };
29597
29598 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29599     
29600     active : false,
29601     title : '',
29602     
29603     // the tabBox that this is attached to.
29604     tab : false,
29605      
29606     getAutoCreate : function() 
29607     {
29608         var cfg = {
29609             tag: 'div',
29610             cls: 'tab-pane'
29611         };
29612         
29613         if(this.active){
29614             cfg.cls += ' active';
29615         }
29616         
29617         return cfg;
29618     },
29619     initEvents  : function()
29620     {
29621         //Roo.log('trigger add pane handler');
29622         this.parent().fireEvent('addpane', this)
29623     },
29624     
29625      /**
29626      * Updates the tab title 
29627      * @param {String} html to set the title to.
29628      */
29629     setTitle: function(str)
29630     {
29631         if (!this.tab) {
29632             return;
29633         }
29634         this.title = str;
29635         this.tab.select('a', true).first().dom.innerHTML = str;
29636         
29637     }
29638     
29639     
29640     
29641 });
29642
29643  
29644
29645
29646  /*
29647  * - LGPL
29648  *
29649  * Tooltip
29650  * 
29651  */
29652
29653 /**
29654  * @class Roo.bootstrap.Tooltip
29655  * Bootstrap Tooltip class
29656  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29657  * to determine which dom element triggers the tooltip.
29658  * 
29659  * It needs to add support for additional attributes like tooltip-position
29660  * 
29661  * @constructor
29662  * Create a new Toolti
29663  * @param {Object} config The config object
29664  */
29665
29666 Roo.bootstrap.Tooltip = function(config){
29667     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29668     
29669     this.alignment = Roo.bootstrap.Tooltip.alignment;
29670     
29671     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29672         this.alignment = config.alignment;
29673     }
29674     
29675 };
29676
29677 Roo.apply(Roo.bootstrap.Tooltip, {
29678     /**
29679      * @function init initialize tooltip monitoring.
29680      * @static
29681      */
29682     currentEl : false,
29683     currentTip : false,
29684     currentRegion : false,
29685     
29686     //  init : delay?
29687     
29688     init : function()
29689     {
29690         Roo.get(document).on('mouseover', this.enter ,this);
29691         Roo.get(document).on('mouseout', this.leave, this);
29692          
29693         
29694         this.currentTip = new Roo.bootstrap.Tooltip();
29695     },
29696     
29697     enter : function(ev)
29698     {
29699         var dom = ev.getTarget();
29700         
29701         //Roo.log(['enter',dom]);
29702         var el = Roo.fly(dom);
29703         if (this.currentEl) {
29704             //Roo.log(dom);
29705             //Roo.log(this.currentEl);
29706             //Roo.log(this.currentEl.contains(dom));
29707             if (this.currentEl == el) {
29708                 return;
29709             }
29710             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29711                 return;
29712             }
29713
29714         }
29715         
29716         if (this.currentTip.el) {
29717             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29718         }    
29719         //Roo.log(ev);
29720         
29721         if(!el || el.dom == document){
29722             return;
29723         }
29724         
29725         var bindEl = el; 
29726         var pel = false;
29727         if (!el.attr('tooltip')) {
29728             pel = el.findParent("[tooltip]");
29729             if (pel) {
29730                 bindEl = Roo.get(pel);
29731             }
29732         }
29733         
29734        
29735         
29736         // you can not look for children, as if el is the body.. then everythign is the child..
29737         if (!pel && !el.attr('tooltip')) { //
29738             if (!el.select("[tooltip]").elements.length) {
29739                 return;
29740             }
29741             // is the mouse over this child...?
29742             bindEl = el.select("[tooltip]").first();
29743             var xy = ev.getXY();
29744             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29745                 //Roo.log("not in region.");
29746                 return;
29747             }
29748             //Roo.log("child element over..");
29749             
29750         }
29751         this.currentEl = el;
29752         this.currentTip.bind(bindEl);
29753         this.currentRegion = Roo.lib.Region.getRegion(dom);
29754         this.currentTip.enter();
29755         
29756     },
29757     leave : function(ev)
29758     {
29759         var dom = ev.getTarget();
29760         //Roo.log(['leave',dom]);
29761         if (!this.currentEl) {
29762             return;
29763         }
29764         
29765         
29766         if (dom != this.currentEl.dom) {
29767             return;
29768         }
29769         var xy = ev.getXY();
29770         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29771             return;
29772         }
29773         // only activate leave if mouse cursor is outside... bounding box..
29774         
29775         
29776         
29777         
29778         if (this.currentTip) {
29779             this.currentTip.leave();
29780         }
29781         //Roo.log('clear currentEl');
29782         this.currentEl = false;
29783         
29784         
29785     },
29786     alignment : {
29787         'left' : ['r-l', [-2,0], 'right'],
29788         'right' : ['l-r', [2,0], 'left'],
29789         'bottom' : ['t-b', [0,2], 'top'],
29790         'top' : [ 'b-t', [0,-2], 'bottom']
29791     }
29792     
29793 });
29794
29795
29796 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29797     
29798     
29799     bindEl : false,
29800     
29801     delay : null, // can be { show : 300 , hide: 500}
29802     
29803     timeout : null,
29804     
29805     hoverState : null, //???
29806     
29807     placement : 'bottom', 
29808     
29809     alignment : false,
29810     
29811     getAutoCreate : function(){
29812     
29813         var cfg = {
29814            cls : 'tooltip',   
29815            role : 'tooltip',
29816            cn : [
29817                 {
29818                     cls : 'tooltip-arrow arrow'
29819                 },
29820                 {
29821                     cls : 'tooltip-inner'
29822                 }
29823            ]
29824         };
29825         
29826         return cfg;
29827     },
29828     bind : function(el)
29829     {
29830         this.bindEl = el;
29831     },
29832     
29833     initEvents : function()
29834     {
29835         this.arrowEl = this.el.select('.arrow', true).first();
29836         this.innerEl = this.el.select('.tooltip-inner', true).first();
29837     },
29838     
29839     enter : function () {
29840        
29841         if (this.timeout != null) {
29842             clearTimeout(this.timeout);
29843         }
29844         
29845         this.hoverState = 'in';
29846          //Roo.log("enter - show");
29847         if (!this.delay || !this.delay.show) {
29848             this.show();
29849             return;
29850         }
29851         var _t = this;
29852         this.timeout = setTimeout(function () {
29853             if (_t.hoverState == 'in') {
29854                 _t.show();
29855             }
29856         }, this.delay.show);
29857     },
29858     leave : function()
29859     {
29860         clearTimeout(this.timeout);
29861     
29862         this.hoverState = 'out';
29863          if (!this.delay || !this.delay.hide) {
29864             this.hide();
29865             return;
29866         }
29867        
29868         var _t = this;
29869         this.timeout = setTimeout(function () {
29870             //Roo.log("leave - timeout");
29871             
29872             if (_t.hoverState == 'out') {
29873                 _t.hide();
29874                 Roo.bootstrap.Tooltip.currentEl = false;
29875             }
29876         }, delay);
29877     },
29878     
29879     show : function (msg)
29880     {
29881         if (!this.el) {
29882             this.render(document.body);
29883         }
29884         // set content.
29885         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29886         
29887         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29888         
29889         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29890         
29891         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29892                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29893         
29894         var placement = typeof this.placement == 'function' ?
29895             this.placement.call(this, this.el, on_el) :
29896             this.placement;
29897             
29898         var autoToken = /\s?auto?\s?/i;
29899         var autoPlace = autoToken.test(placement);
29900         if (autoPlace) {
29901             placement = placement.replace(autoToken, '') || 'top';
29902         }
29903         
29904         //this.el.detach()
29905         //this.el.setXY([0,0]);
29906         this.el.show();
29907         //this.el.dom.style.display='block';
29908         
29909         //this.el.appendTo(on_el);
29910         
29911         var p = this.getPosition();
29912         var box = this.el.getBox();
29913         
29914         if (autoPlace) {
29915             // fixme..
29916         }
29917         
29918         var align = this.alignment[placement];
29919         
29920         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29921         
29922         if(placement == 'top' || placement == 'bottom'){
29923             if(xy[0] < 0){
29924                 placement = 'right';
29925             }
29926             
29927             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29928                 placement = 'left';
29929             }
29930             
29931             var scroll = Roo.select('body', true).first().getScroll();
29932             
29933             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29934                 placement = 'top';
29935             }
29936             
29937             align = this.alignment[placement];
29938             
29939             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29940             
29941         }
29942         
29943         var elems = document.getElementsByTagName('div');
29944         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29945         for (var i = 0; i < elems.length; i++) {
29946           var zindex = Number.parseInt(
29947                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29948                 10
29949           );
29950           if (zindex > highest) {
29951             highest = zindex;
29952           }
29953         }
29954         
29955         
29956         
29957         this.el.dom.style.zIndex = highest;
29958         
29959         this.el.alignTo(this.bindEl, align[0],align[1]);
29960         //var arrow = this.el.select('.arrow',true).first();
29961         //arrow.set(align[2], 
29962         
29963         this.el.addClass(placement);
29964         this.el.addClass("bs-tooltip-"+ placement);
29965         
29966         this.el.addClass('in fade show');
29967         
29968         this.hoverState = null;
29969         
29970         if (this.el.hasClass('fade')) {
29971             // fade it?
29972         }
29973         
29974         
29975         
29976         
29977         
29978     },
29979     hide : function()
29980     {
29981          
29982         if (!this.el) {
29983             return;
29984         }
29985         //this.el.setXY([0,0]);
29986         this.el.removeClass(['show', 'in']);
29987         //this.el.hide();
29988         
29989     }
29990     
29991 });
29992  
29993
29994  /*
29995  * - LGPL
29996  *
29997  * Location Picker
29998  * 
29999  */
30000
30001 /**
30002  * @class Roo.bootstrap.LocationPicker
30003  * @extends Roo.bootstrap.Component
30004  * Bootstrap LocationPicker class
30005  * @cfg {Number} latitude Position when init default 0
30006  * @cfg {Number} longitude Position when init default 0
30007  * @cfg {Number} zoom default 15
30008  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
30009  * @cfg {Boolean} mapTypeControl default false
30010  * @cfg {Boolean} disableDoubleClickZoom default false
30011  * @cfg {Boolean} scrollwheel default true
30012  * @cfg {Boolean} streetViewControl default false
30013  * @cfg {Number} radius default 0
30014  * @cfg {String} locationName
30015  * @cfg {Boolean} draggable default true
30016  * @cfg {Boolean} enableAutocomplete default false
30017  * @cfg {Boolean} enableReverseGeocode default true
30018  * @cfg {String} markerTitle
30019  * 
30020  * @constructor
30021  * Create a new LocationPicker
30022  * @param {Object} config The config object
30023  */
30024
30025
30026 Roo.bootstrap.LocationPicker = function(config){
30027     
30028     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30029     
30030     this.addEvents({
30031         /**
30032          * @event initial
30033          * Fires when the picker initialized.
30034          * @param {Roo.bootstrap.LocationPicker} this
30035          * @param {Google Location} location
30036          */
30037         initial : true,
30038         /**
30039          * @event positionchanged
30040          * Fires when the picker position changed.
30041          * @param {Roo.bootstrap.LocationPicker} this
30042          * @param {Google Location} location
30043          */
30044         positionchanged : true,
30045         /**
30046          * @event resize
30047          * Fires when the map resize.
30048          * @param {Roo.bootstrap.LocationPicker} this
30049          */
30050         resize : true,
30051         /**
30052          * @event show
30053          * Fires when the map show.
30054          * @param {Roo.bootstrap.LocationPicker} this
30055          */
30056         show : true,
30057         /**
30058          * @event hide
30059          * Fires when the map hide.
30060          * @param {Roo.bootstrap.LocationPicker} this
30061          */
30062         hide : true,
30063         /**
30064          * @event mapClick
30065          * Fires when click the map.
30066          * @param {Roo.bootstrap.LocationPicker} this
30067          * @param {Map event} e
30068          */
30069         mapClick : true,
30070         /**
30071          * @event mapRightClick
30072          * Fires when right click the map.
30073          * @param {Roo.bootstrap.LocationPicker} this
30074          * @param {Map event} e
30075          */
30076         mapRightClick : true,
30077         /**
30078          * @event markerClick
30079          * Fires when click the marker.
30080          * @param {Roo.bootstrap.LocationPicker} this
30081          * @param {Map event} e
30082          */
30083         markerClick : true,
30084         /**
30085          * @event markerRightClick
30086          * Fires when right click the marker.
30087          * @param {Roo.bootstrap.LocationPicker} this
30088          * @param {Map event} e
30089          */
30090         markerRightClick : true,
30091         /**
30092          * @event OverlayViewDraw
30093          * Fires when OverlayView Draw
30094          * @param {Roo.bootstrap.LocationPicker} this
30095          */
30096         OverlayViewDraw : true,
30097         /**
30098          * @event OverlayViewOnAdd
30099          * Fires when OverlayView Draw
30100          * @param {Roo.bootstrap.LocationPicker} this
30101          */
30102         OverlayViewOnAdd : true,
30103         /**
30104          * @event OverlayViewOnRemove
30105          * Fires when OverlayView Draw
30106          * @param {Roo.bootstrap.LocationPicker} this
30107          */
30108         OverlayViewOnRemove : true,
30109         /**
30110          * @event OverlayViewShow
30111          * Fires when OverlayView Draw
30112          * @param {Roo.bootstrap.LocationPicker} this
30113          * @param {Pixel} cpx
30114          */
30115         OverlayViewShow : true,
30116         /**
30117          * @event OverlayViewHide
30118          * Fires when OverlayView Draw
30119          * @param {Roo.bootstrap.LocationPicker} this
30120          */
30121         OverlayViewHide : true,
30122         /**
30123          * @event loadexception
30124          * Fires when load google lib failed.
30125          * @param {Roo.bootstrap.LocationPicker} this
30126          */
30127         loadexception : true
30128     });
30129         
30130 };
30131
30132 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30133     
30134     gMapContext: false,
30135     
30136     latitude: 0,
30137     longitude: 0,
30138     zoom: 15,
30139     mapTypeId: false,
30140     mapTypeControl: false,
30141     disableDoubleClickZoom: false,
30142     scrollwheel: true,
30143     streetViewControl: false,
30144     radius: 0,
30145     locationName: '',
30146     draggable: true,
30147     enableAutocomplete: false,
30148     enableReverseGeocode: true,
30149     markerTitle: '',
30150     
30151     getAutoCreate: function()
30152     {
30153
30154         var cfg = {
30155             tag: 'div',
30156             cls: 'roo-location-picker'
30157         };
30158         
30159         return cfg
30160     },
30161     
30162     initEvents: function(ct, position)
30163     {       
30164         if(!this.el.getWidth() || this.isApplied()){
30165             return;
30166         }
30167         
30168         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30169         
30170         this.initial();
30171     },
30172     
30173     initial: function()
30174     {
30175         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30176             this.fireEvent('loadexception', this);
30177             return;
30178         }
30179         
30180         if(!this.mapTypeId){
30181             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30182         }
30183         
30184         this.gMapContext = this.GMapContext();
30185         
30186         this.initOverlayView();
30187         
30188         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30189         
30190         var _this = this;
30191                 
30192         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30193             _this.setPosition(_this.gMapContext.marker.position);
30194         });
30195         
30196         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30197             _this.fireEvent('mapClick', this, event);
30198             
30199         });
30200
30201         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30202             _this.fireEvent('mapRightClick', this, event);
30203             
30204         });
30205         
30206         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30207             _this.fireEvent('markerClick', this, event);
30208             
30209         });
30210
30211         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30212             _this.fireEvent('markerRightClick', this, event);
30213             
30214         });
30215         
30216         this.setPosition(this.gMapContext.location);
30217         
30218         this.fireEvent('initial', this, this.gMapContext.location);
30219     },
30220     
30221     initOverlayView: function()
30222     {
30223         var _this = this;
30224         
30225         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30226             
30227             draw: function()
30228             {
30229                 _this.fireEvent('OverlayViewDraw', _this);
30230             },
30231             
30232             onAdd: function()
30233             {
30234                 _this.fireEvent('OverlayViewOnAdd', _this);
30235             },
30236             
30237             onRemove: function()
30238             {
30239                 _this.fireEvent('OverlayViewOnRemove', _this);
30240             },
30241             
30242             show: function(cpx)
30243             {
30244                 _this.fireEvent('OverlayViewShow', _this, cpx);
30245             },
30246             
30247             hide: function()
30248             {
30249                 _this.fireEvent('OverlayViewHide', _this);
30250             }
30251             
30252         });
30253     },
30254     
30255     fromLatLngToContainerPixel: function(event)
30256     {
30257         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30258     },
30259     
30260     isApplied: function() 
30261     {
30262         return this.getGmapContext() == false ? false : true;
30263     },
30264     
30265     getGmapContext: function() 
30266     {
30267         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30268     },
30269     
30270     GMapContext: function() 
30271     {
30272         var position = new google.maps.LatLng(this.latitude, this.longitude);
30273         
30274         var _map = new google.maps.Map(this.el.dom, {
30275             center: position,
30276             zoom: this.zoom,
30277             mapTypeId: this.mapTypeId,
30278             mapTypeControl: this.mapTypeControl,
30279             disableDoubleClickZoom: this.disableDoubleClickZoom,
30280             scrollwheel: this.scrollwheel,
30281             streetViewControl: this.streetViewControl,
30282             locationName: this.locationName,
30283             draggable: this.draggable,
30284             enableAutocomplete: this.enableAutocomplete,
30285             enableReverseGeocode: this.enableReverseGeocode
30286         });
30287         
30288         var _marker = new google.maps.Marker({
30289             position: position,
30290             map: _map,
30291             title: this.markerTitle,
30292             draggable: this.draggable
30293         });
30294         
30295         return {
30296             map: _map,
30297             marker: _marker,
30298             circle: null,
30299             location: position,
30300             radius: this.radius,
30301             locationName: this.locationName,
30302             addressComponents: {
30303                 formatted_address: null,
30304                 addressLine1: null,
30305                 addressLine2: null,
30306                 streetName: null,
30307                 streetNumber: null,
30308                 city: null,
30309                 district: null,
30310                 state: null,
30311                 stateOrProvince: null
30312             },
30313             settings: this,
30314             domContainer: this.el.dom,
30315             geodecoder: new google.maps.Geocoder()
30316         };
30317     },
30318     
30319     drawCircle: function(center, radius, options) 
30320     {
30321         if (this.gMapContext.circle != null) {
30322             this.gMapContext.circle.setMap(null);
30323         }
30324         if (radius > 0) {
30325             radius *= 1;
30326             options = Roo.apply({}, options, {
30327                 strokeColor: "#0000FF",
30328                 strokeOpacity: .35,
30329                 strokeWeight: 2,
30330                 fillColor: "#0000FF",
30331                 fillOpacity: .2
30332             });
30333             
30334             options.map = this.gMapContext.map;
30335             options.radius = radius;
30336             options.center = center;
30337             this.gMapContext.circle = new google.maps.Circle(options);
30338             return this.gMapContext.circle;
30339         }
30340         
30341         return null;
30342     },
30343     
30344     setPosition: function(location) 
30345     {
30346         this.gMapContext.location = location;
30347         this.gMapContext.marker.setPosition(location);
30348         this.gMapContext.map.panTo(location);
30349         this.drawCircle(location, this.gMapContext.radius, {});
30350         
30351         var _this = this;
30352         
30353         if (this.gMapContext.settings.enableReverseGeocode) {
30354             this.gMapContext.geodecoder.geocode({
30355                 latLng: this.gMapContext.location
30356             }, function(results, status) {
30357                 
30358                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30359                     _this.gMapContext.locationName = results[0].formatted_address;
30360                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30361                     
30362                     _this.fireEvent('positionchanged', this, location);
30363                 }
30364             });
30365             
30366             return;
30367         }
30368         
30369         this.fireEvent('positionchanged', this, location);
30370     },
30371     
30372     resize: function()
30373     {
30374         google.maps.event.trigger(this.gMapContext.map, "resize");
30375         
30376         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30377         
30378         this.fireEvent('resize', this);
30379     },
30380     
30381     setPositionByLatLng: function(latitude, longitude)
30382     {
30383         this.setPosition(new google.maps.LatLng(latitude, longitude));
30384     },
30385     
30386     getCurrentPosition: function() 
30387     {
30388         return {
30389             latitude: this.gMapContext.location.lat(),
30390             longitude: this.gMapContext.location.lng()
30391         };
30392     },
30393     
30394     getAddressName: function() 
30395     {
30396         return this.gMapContext.locationName;
30397     },
30398     
30399     getAddressComponents: function() 
30400     {
30401         return this.gMapContext.addressComponents;
30402     },
30403     
30404     address_component_from_google_geocode: function(address_components) 
30405     {
30406         var result = {};
30407         
30408         for (var i = 0; i < address_components.length; i++) {
30409             var component = address_components[i];
30410             if (component.types.indexOf("postal_code") >= 0) {
30411                 result.postalCode = component.short_name;
30412             } else if (component.types.indexOf("street_number") >= 0) {
30413                 result.streetNumber = component.short_name;
30414             } else if (component.types.indexOf("route") >= 0) {
30415                 result.streetName = component.short_name;
30416             } else if (component.types.indexOf("neighborhood") >= 0) {
30417                 result.city = component.short_name;
30418             } else if (component.types.indexOf("locality") >= 0) {
30419                 result.city = component.short_name;
30420             } else if (component.types.indexOf("sublocality") >= 0) {
30421                 result.district = component.short_name;
30422             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30423                 result.stateOrProvince = component.short_name;
30424             } else if (component.types.indexOf("country") >= 0) {
30425                 result.country = component.short_name;
30426             }
30427         }
30428         
30429         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30430         result.addressLine2 = "";
30431         return result;
30432     },
30433     
30434     setZoomLevel: function(zoom)
30435     {
30436         this.gMapContext.map.setZoom(zoom);
30437     },
30438     
30439     show: function()
30440     {
30441         if(!this.el){
30442             return;
30443         }
30444         
30445         this.el.show();
30446         
30447         this.resize();
30448         
30449         this.fireEvent('show', this);
30450     },
30451     
30452     hide: function()
30453     {
30454         if(!this.el){
30455             return;
30456         }
30457         
30458         this.el.hide();
30459         
30460         this.fireEvent('hide', this);
30461     }
30462     
30463 });
30464
30465 Roo.apply(Roo.bootstrap.LocationPicker, {
30466     
30467     OverlayView : function(map, options)
30468     {
30469         options = options || {};
30470         
30471         this.setMap(map);
30472     }
30473     
30474     
30475 });/**
30476  * @class Roo.bootstrap.Alert
30477  * @extends Roo.bootstrap.Component
30478  * Bootstrap Alert class - shows an alert area box
30479  * eg
30480  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30481   Enter a valid email address
30482 </div>
30483  * @licence LGPL
30484  * @cfg {String} title The title of alert
30485  * @cfg {String} html The content of alert
30486  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30487  * @cfg {String} fa font-awesomeicon
30488  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30489  * @cfg {Boolean} close true to show a x closer
30490  * 
30491  * 
30492  * @constructor
30493  * Create a new alert
30494  * @param {Object} config The config object
30495  */
30496
30497
30498 Roo.bootstrap.Alert = function(config){
30499     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30500     
30501 };
30502
30503 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30504     
30505     title: '',
30506     html: '',
30507     weight: false,
30508     fa: false,
30509     faicon: false, // BC
30510     close : false,
30511     
30512     
30513     getAutoCreate : function()
30514     {
30515         
30516         var cfg = {
30517             tag : 'div',
30518             cls : 'alert',
30519             cn : [
30520                 {
30521                     tag: 'button',
30522                     type :  "button",
30523                     cls: "close",
30524                     html : '×',
30525                     style : this.close ? '' : 'display:none'
30526                 },
30527                 {
30528                     tag : 'i',
30529                     cls : 'roo-alert-icon'
30530                     
30531                 },
30532                 {
30533                     tag : 'b',
30534                     cls : 'roo-alert-title',
30535                     html : this.title
30536                 },
30537                 {
30538                     tag : 'span',
30539                     cls : 'roo-alert-text',
30540                     html : this.html
30541                 }
30542             ]
30543         };
30544         
30545         if(this.faicon){
30546             cfg.cn[0].cls += ' fa ' + this.faicon;
30547         }
30548         if(this.fa){
30549             cfg.cn[0].cls += ' fa ' + this.fa;
30550         }
30551         
30552         if(this.weight){
30553             cfg.cls += ' alert-' + this.weight;
30554         }
30555         
30556         return cfg;
30557     },
30558     
30559     initEvents: function() 
30560     {
30561         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30562         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30563         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30564         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30565         if (this.seconds > 0) {
30566             this.hide.defer(this.seconds, this);
30567         }
30568     },
30569     /**
30570      * Set the Title Message HTML
30571      * @param {String} html
30572      */
30573     setTitle : function(str)
30574     {
30575         this.titleEl.dom.innerHTML = str;
30576     },
30577      
30578      /**
30579      * Set the Body Message HTML
30580      * @param {String} html
30581      */
30582     setHtml : function(str)
30583     {
30584         this.htmlEl.dom.innerHTML = str;
30585     },
30586     /**
30587      * Set the Weight of the alert
30588      * @param {String} (success|info|warning|danger) weight
30589      */
30590     
30591     setWeight : function(weight)
30592     {
30593         if(this.weight){
30594             this.el.removeClass('alert-' + this.weight);
30595         }
30596         
30597         this.weight = weight;
30598         
30599         this.el.addClass('alert-' + this.weight);
30600     },
30601       /**
30602      * Set the Icon of the alert
30603      * @param {String} see fontawsome names (name without the 'fa-' bit)
30604      */
30605     setIcon : function(icon)
30606     {
30607         if(this.faicon){
30608             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30609         }
30610         
30611         this.faicon = icon;
30612         
30613         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30614     },
30615     /**
30616      * Hide the Alert
30617      */
30618     hide: function() 
30619     {
30620         this.el.hide();   
30621     },
30622     /**
30623      * Show the Alert
30624      */
30625     show: function() 
30626     {  
30627         this.el.show();   
30628     }
30629     
30630 });
30631
30632  
30633 /*
30634 * Licence: LGPL
30635 */
30636
30637 /**
30638  * @class Roo.bootstrap.UploadCropbox
30639  * @extends Roo.bootstrap.Component
30640  * Bootstrap UploadCropbox class
30641  * @cfg {String} emptyText show when image has been loaded
30642  * @cfg {String} rotateNotify show when image too small to rotate
30643  * @cfg {Number} errorTimeout default 3000
30644  * @cfg {Number} minWidth default 300
30645  * @cfg {Number} minHeight default 300
30646  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30647  * @cfg {Boolean} isDocument (true|false) default false
30648  * @cfg {String} url action url
30649  * @cfg {String} paramName default 'imageUpload'
30650  * @cfg {String} method default POST
30651  * @cfg {Boolean} loadMask (true|false) default true
30652  * @cfg {Boolean} loadingText default 'Loading...'
30653  * 
30654  * @constructor
30655  * Create a new UploadCropbox
30656  * @param {Object} config The config object
30657  */
30658
30659 Roo.bootstrap.UploadCropbox = function(config){
30660     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30661     
30662     this.addEvents({
30663         /**
30664          * @event beforeselectfile
30665          * Fire before select file
30666          * @param {Roo.bootstrap.UploadCropbox} this
30667          */
30668         "beforeselectfile" : true,
30669         /**
30670          * @event initial
30671          * Fire after initEvent
30672          * @param {Roo.bootstrap.UploadCropbox} this
30673          */
30674         "initial" : true,
30675         /**
30676          * @event crop
30677          * Fire after initEvent
30678          * @param {Roo.bootstrap.UploadCropbox} this
30679          * @param {String} data
30680          */
30681         "crop" : true,
30682         /**
30683          * @event prepare
30684          * Fire when preparing the file data
30685          * @param {Roo.bootstrap.UploadCropbox} this
30686          * @param {Object} file
30687          */
30688         "prepare" : true,
30689         /**
30690          * @event exception
30691          * Fire when get exception
30692          * @param {Roo.bootstrap.UploadCropbox} this
30693          * @param {XMLHttpRequest} xhr
30694          */
30695         "exception" : true,
30696         /**
30697          * @event beforeloadcanvas
30698          * Fire before load the canvas
30699          * @param {Roo.bootstrap.UploadCropbox} this
30700          * @param {String} src
30701          */
30702         "beforeloadcanvas" : true,
30703         /**
30704          * @event trash
30705          * Fire when trash image
30706          * @param {Roo.bootstrap.UploadCropbox} this
30707          */
30708         "trash" : true,
30709         /**
30710          * @event download
30711          * Fire when download the image
30712          * @param {Roo.bootstrap.UploadCropbox} this
30713          */
30714         "download" : true,
30715         /**
30716          * @event footerbuttonclick
30717          * Fire when footerbuttonclick
30718          * @param {Roo.bootstrap.UploadCropbox} this
30719          * @param {String} type
30720          */
30721         "footerbuttonclick" : true,
30722         /**
30723          * @event resize
30724          * Fire when resize
30725          * @param {Roo.bootstrap.UploadCropbox} this
30726          */
30727         "resize" : true,
30728         /**
30729          * @event rotate
30730          * Fire when rotate the image
30731          * @param {Roo.bootstrap.UploadCropbox} this
30732          * @param {String} pos
30733          */
30734         "rotate" : true,
30735         /**
30736          * @event inspect
30737          * Fire when inspect the file
30738          * @param {Roo.bootstrap.UploadCropbox} this
30739          * @param {Object} file
30740          */
30741         "inspect" : true,
30742         /**
30743          * @event upload
30744          * Fire when xhr upload the file
30745          * @param {Roo.bootstrap.UploadCropbox} this
30746          * @param {Object} data
30747          */
30748         "upload" : true,
30749         /**
30750          * @event arrange
30751          * Fire when arrange the file data
30752          * @param {Roo.bootstrap.UploadCropbox} this
30753          * @param {Object} formData
30754          */
30755         "arrange" : true
30756     });
30757     
30758     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30759 };
30760
30761 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30762     
30763     emptyText : 'Click to upload image',
30764     rotateNotify : 'Image is too small to rotate',
30765     errorTimeout : 3000,
30766     scale : 0,
30767     baseScale : 1,
30768     rotate : 0,
30769     dragable : false,
30770     pinching : false,
30771     mouseX : 0,
30772     mouseY : 0,
30773     cropData : false,
30774     minWidth : 300,
30775     minHeight : 300,
30776     file : false,
30777     exif : {},
30778     baseRotate : 1,
30779     cropType : 'image/jpeg',
30780     buttons : false,
30781     canvasLoaded : false,
30782     isDocument : false,
30783     method : 'POST',
30784     paramName : 'imageUpload',
30785     loadMask : true,
30786     loadingText : 'Loading...',
30787     maskEl : false,
30788     
30789     getAutoCreate : function()
30790     {
30791         var cfg = {
30792             tag : 'div',
30793             cls : 'roo-upload-cropbox',
30794             cn : [
30795                 {
30796                     tag : 'input',
30797                     cls : 'roo-upload-cropbox-selector',
30798                     type : 'file'
30799                 },
30800                 {
30801                     tag : 'div',
30802                     cls : 'roo-upload-cropbox-body',
30803                     style : 'cursor:pointer',
30804                     cn : [
30805                         {
30806                             tag : 'div',
30807                             cls : 'roo-upload-cropbox-preview'
30808                         },
30809                         {
30810                             tag : 'div',
30811                             cls : 'roo-upload-cropbox-thumb'
30812                         },
30813                         {
30814                             tag : 'div',
30815                             cls : 'roo-upload-cropbox-empty-notify',
30816                             html : this.emptyText
30817                         },
30818                         {
30819                             tag : 'div',
30820                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30821                             html : this.rotateNotify
30822                         }
30823                     ]
30824                 },
30825                 {
30826                     tag : 'div',
30827                     cls : 'roo-upload-cropbox-footer',
30828                     cn : {
30829                         tag : 'div',
30830                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30831                         cn : []
30832                     }
30833                 }
30834             ]
30835         };
30836         
30837         return cfg;
30838     },
30839     
30840     onRender : function(ct, position)
30841     {
30842         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30843         
30844         if (this.buttons.length) {
30845             
30846             Roo.each(this.buttons, function(bb) {
30847                 
30848                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30849                 
30850                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30851                 
30852             }, this);
30853         }
30854         
30855         if(this.loadMask){
30856             this.maskEl = this.el;
30857         }
30858     },
30859     
30860     initEvents : function()
30861     {
30862         this.urlAPI = (window.createObjectURL && window) || 
30863                                 (window.URL && URL.revokeObjectURL && URL) || 
30864                                 (window.webkitURL && webkitURL);
30865                         
30866         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30867         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30868         
30869         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30870         this.selectorEl.hide();
30871         
30872         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30873         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30874         
30875         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30876         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30877         this.thumbEl.hide();
30878         
30879         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30880         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30881         
30882         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30883         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30884         this.errorEl.hide();
30885         
30886         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30887         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30888         this.footerEl.hide();
30889         
30890         this.setThumbBoxSize();
30891         
30892         this.bind();
30893         
30894         this.resize();
30895         
30896         this.fireEvent('initial', this);
30897     },
30898
30899     bind : function()
30900     {
30901         var _this = this;
30902         
30903         window.addEventListener("resize", function() { _this.resize(); } );
30904         
30905         this.bodyEl.on('click', this.beforeSelectFile, this);
30906         
30907         if(Roo.isTouch){
30908             this.bodyEl.on('touchstart', this.onTouchStart, this);
30909             this.bodyEl.on('touchmove', this.onTouchMove, this);
30910             this.bodyEl.on('touchend', this.onTouchEnd, this);
30911         }
30912         
30913         if(!Roo.isTouch){
30914             this.bodyEl.on('mousedown', this.onMouseDown, this);
30915             this.bodyEl.on('mousemove', this.onMouseMove, this);
30916             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30917             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30918             Roo.get(document).on('mouseup', this.onMouseUp, this);
30919         }
30920         
30921         this.selectorEl.on('change', this.onFileSelected, this);
30922     },
30923     
30924     reset : function()
30925     {    
30926         this.scale = 0;
30927         this.baseScale = 1;
30928         this.rotate = 0;
30929         this.baseRotate = 1;
30930         this.dragable = false;
30931         this.pinching = false;
30932         this.mouseX = 0;
30933         this.mouseY = 0;
30934         this.cropData = false;
30935         this.notifyEl.dom.innerHTML = this.emptyText;
30936         
30937         this.selectorEl.dom.value = '';
30938         
30939     },
30940     
30941     resize : function()
30942     {
30943         if(this.fireEvent('resize', this) != false){
30944             this.setThumbBoxPosition();
30945             this.setCanvasPosition();
30946         }
30947     },
30948     
30949     onFooterButtonClick : function(e, el, o, type)
30950     {
30951         switch (type) {
30952             case 'rotate-left' :
30953                 this.onRotateLeft(e);
30954                 break;
30955             case 'rotate-right' :
30956                 this.onRotateRight(e);
30957                 break;
30958             case 'picture' :
30959                 this.beforeSelectFile(e);
30960                 break;
30961             case 'trash' :
30962                 this.trash(e);
30963                 break;
30964             case 'crop' :
30965                 this.crop(e);
30966                 break;
30967             case 'download' :
30968                 this.download(e);
30969                 break;
30970             default :
30971                 break;
30972         }
30973         
30974         this.fireEvent('footerbuttonclick', this, type);
30975     },
30976     
30977     beforeSelectFile : function(e)
30978     {
30979         e.preventDefault();
30980         
30981         if(this.fireEvent('beforeselectfile', this) != false){
30982             this.selectorEl.dom.click();
30983         }
30984     },
30985     
30986     onFileSelected : function(e)
30987     {
30988         e.preventDefault();
30989         
30990         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30991             return;
30992         }
30993         
30994         var file = this.selectorEl.dom.files[0];
30995         
30996         if(this.fireEvent('inspect', this, file) != false){
30997             this.prepare(file);
30998         }
30999         
31000     },
31001     
31002     trash : function(e)
31003     {
31004         this.fireEvent('trash', this);
31005     },
31006     
31007     download : function(e)
31008     {
31009         this.fireEvent('download', this);
31010     },
31011     
31012     loadCanvas : function(src)
31013     {   
31014         if(this.fireEvent('beforeloadcanvas', this, src) != false){
31015             
31016             this.reset();
31017             
31018             this.imageEl = document.createElement('img');
31019             
31020             var _this = this;
31021             
31022             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31023             
31024             this.imageEl.src = src;
31025         }
31026     },
31027     
31028     onLoadCanvas : function()
31029     {   
31030         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31031         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31032         
31033         this.bodyEl.un('click', this.beforeSelectFile, this);
31034         
31035         this.notifyEl.hide();
31036         this.thumbEl.show();
31037         this.footerEl.show();
31038         
31039         this.baseRotateLevel();
31040         
31041         if(this.isDocument){
31042             this.setThumbBoxSize();
31043         }
31044         
31045         this.setThumbBoxPosition();
31046         
31047         this.baseScaleLevel();
31048         
31049         this.draw();
31050         
31051         this.resize();
31052         
31053         this.canvasLoaded = true;
31054         
31055         if(this.loadMask){
31056             this.maskEl.unmask();
31057         }
31058         
31059     },
31060     
31061     setCanvasPosition : function()
31062     {   
31063         if(!this.canvasEl){
31064             return;
31065         }
31066         
31067         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31068         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31069         
31070         this.previewEl.setLeft(pw);
31071         this.previewEl.setTop(ph);
31072         
31073     },
31074     
31075     onMouseDown : function(e)
31076     {   
31077         e.stopEvent();
31078         
31079         this.dragable = true;
31080         this.pinching = false;
31081         
31082         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31083             this.dragable = false;
31084             return;
31085         }
31086         
31087         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31088         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31089         
31090     },
31091     
31092     onMouseMove : function(e)
31093     {   
31094         e.stopEvent();
31095         
31096         if(!this.canvasLoaded){
31097             return;
31098         }
31099         
31100         if (!this.dragable){
31101             return;
31102         }
31103         
31104         var minX = Math.ceil(this.thumbEl.getLeft(true));
31105         var minY = Math.ceil(this.thumbEl.getTop(true));
31106         
31107         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31108         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31109         
31110         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31111         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31112         
31113         x = x - this.mouseX;
31114         y = y - this.mouseY;
31115         
31116         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31117         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31118         
31119         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31120         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31121         
31122         this.previewEl.setLeft(bgX);
31123         this.previewEl.setTop(bgY);
31124         
31125         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31126         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31127     },
31128     
31129     onMouseUp : function(e)
31130     {   
31131         e.stopEvent();
31132         
31133         this.dragable = false;
31134     },
31135     
31136     onMouseWheel : function(e)
31137     {   
31138         e.stopEvent();
31139         
31140         this.startScale = this.scale;
31141         
31142         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31143         
31144         if(!this.zoomable()){
31145             this.scale = this.startScale;
31146             return;
31147         }
31148         
31149         this.draw();
31150         
31151         return;
31152     },
31153     
31154     zoomable : function()
31155     {
31156         var minScale = this.thumbEl.getWidth() / this.minWidth;
31157         
31158         if(this.minWidth < this.minHeight){
31159             minScale = this.thumbEl.getHeight() / this.minHeight;
31160         }
31161         
31162         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31163         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31164         
31165         if(
31166                 this.isDocument &&
31167                 (this.rotate == 0 || this.rotate == 180) && 
31168                 (
31169                     width > this.imageEl.OriginWidth || 
31170                     height > this.imageEl.OriginHeight ||
31171                     (width < this.minWidth && height < this.minHeight)
31172                 )
31173         ){
31174             return false;
31175         }
31176         
31177         if(
31178                 this.isDocument &&
31179                 (this.rotate == 90 || this.rotate == 270) && 
31180                 (
31181                     width > this.imageEl.OriginWidth || 
31182                     height > this.imageEl.OriginHeight ||
31183                     (width < this.minHeight && height < this.minWidth)
31184                 )
31185         ){
31186             return false;
31187         }
31188         
31189         if(
31190                 !this.isDocument &&
31191                 (this.rotate == 0 || this.rotate == 180) && 
31192                 (
31193                     width < this.minWidth || 
31194                     width > this.imageEl.OriginWidth || 
31195                     height < this.minHeight || 
31196                     height > this.imageEl.OriginHeight
31197                 )
31198         ){
31199             return false;
31200         }
31201         
31202         if(
31203                 !this.isDocument &&
31204                 (this.rotate == 90 || this.rotate == 270) && 
31205                 (
31206                     width < this.minHeight || 
31207                     width > this.imageEl.OriginWidth || 
31208                     height < this.minWidth || 
31209                     height > this.imageEl.OriginHeight
31210                 )
31211         ){
31212             return false;
31213         }
31214         
31215         return true;
31216         
31217     },
31218     
31219     onRotateLeft : function(e)
31220     {   
31221         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31222             
31223             var minScale = this.thumbEl.getWidth() / this.minWidth;
31224             
31225             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31226             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31227             
31228             this.startScale = this.scale;
31229             
31230             while (this.getScaleLevel() < minScale){
31231             
31232                 this.scale = this.scale + 1;
31233                 
31234                 if(!this.zoomable()){
31235                     break;
31236                 }
31237                 
31238                 if(
31239                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31240                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31241                 ){
31242                     continue;
31243                 }
31244                 
31245                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31246
31247                 this.draw();
31248                 
31249                 return;
31250             }
31251             
31252             this.scale = this.startScale;
31253             
31254             this.onRotateFail();
31255             
31256             return false;
31257         }
31258         
31259         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31260
31261         if(this.isDocument){
31262             this.setThumbBoxSize();
31263             this.setThumbBoxPosition();
31264             this.setCanvasPosition();
31265         }
31266         
31267         this.draw();
31268         
31269         this.fireEvent('rotate', this, 'left');
31270         
31271     },
31272     
31273     onRotateRight : function(e)
31274     {
31275         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31276             
31277             var minScale = this.thumbEl.getWidth() / this.minWidth;
31278         
31279             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31280             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31281             
31282             this.startScale = this.scale;
31283             
31284             while (this.getScaleLevel() < minScale){
31285             
31286                 this.scale = this.scale + 1;
31287                 
31288                 if(!this.zoomable()){
31289                     break;
31290                 }
31291                 
31292                 if(
31293                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31294                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31295                 ){
31296                     continue;
31297                 }
31298                 
31299                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31300
31301                 this.draw();
31302                 
31303                 return;
31304             }
31305             
31306             this.scale = this.startScale;
31307             
31308             this.onRotateFail();
31309             
31310             return false;
31311         }
31312         
31313         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31314
31315         if(this.isDocument){
31316             this.setThumbBoxSize();
31317             this.setThumbBoxPosition();
31318             this.setCanvasPosition();
31319         }
31320         
31321         this.draw();
31322         
31323         this.fireEvent('rotate', this, 'right');
31324     },
31325     
31326     onRotateFail : function()
31327     {
31328         this.errorEl.show(true);
31329         
31330         var _this = this;
31331         
31332         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31333     },
31334     
31335     draw : function()
31336     {
31337         this.previewEl.dom.innerHTML = '';
31338         
31339         var canvasEl = document.createElement("canvas");
31340         
31341         var contextEl = canvasEl.getContext("2d");
31342         
31343         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31344         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31345         var center = this.imageEl.OriginWidth / 2;
31346         
31347         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31348             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31349             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31350             center = this.imageEl.OriginHeight / 2;
31351         }
31352         
31353         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31354         
31355         contextEl.translate(center, center);
31356         contextEl.rotate(this.rotate * Math.PI / 180);
31357
31358         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31359         
31360         this.canvasEl = document.createElement("canvas");
31361         
31362         this.contextEl = this.canvasEl.getContext("2d");
31363         
31364         switch (this.rotate) {
31365             case 0 :
31366                 
31367                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31368                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31369                 
31370                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31371                 
31372                 break;
31373             case 90 : 
31374                 
31375                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31376                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31377                 
31378                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31379                     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);
31380                     break;
31381                 }
31382                 
31383                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31384                 
31385                 break;
31386             case 180 :
31387                 
31388                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31389                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31390                 
31391                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31392                     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);
31393                     break;
31394                 }
31395                 
31396                 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);
31397                 
31398                 break;
31399             case 270 :
31400                 
31401                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31402                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31403         
31404                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31405                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31406                     break;
31407                 }
31408                 
31409                 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);
31410                 
31411                 break;
31412             default : 
31413                 break;
31414         }
31415         
31416         this.previewEl.appendChild(this.canvasEl);
31417         
31418         this.setCanvasPosition();
31419     },
31420     
31421     crop : function()
31422     {
31423         if(!this.canvasLoaded){
31424             return;
31425         }
31426         
31427         var imageCanvas = document.createElement("canvas");
31428         
31429         var imageContext = imageCanvas.getContext("2d");
31430         
31431         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31432         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31433         
31434         var center = imageCanvas.width / 2;
31435         
31436         imageContext.translate(center, center);
31437         
31438         imageContext.rotate(this.rotate * Math.PI / 180);
31439         
31440         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31441         
31442         var canvas = document.createElement("canvas");
31443         
31444         var context = canvas.getContext("2d");
31445                 
31446         canvas.width = this.minWidth;
31447         canvas.height = this.minHeight;
31448
31449         switch (this.rotate) {
31450             case 0 :
31451                 
31452                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31453                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31454                 
31455                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31456                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31457                 
31458                 var targetWidth = this.minWidth - 2 * x;
31459                 var targetHeight = this.minHeight - 2 * y;
31460                 
31461                 var scale = 1;
31462                 
31463                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31464                     scale = targetWidth / width;
31465                 }
31466                 
31467                 if(x > 0 && y == 0){
31468                     scale = targetHeight / height;
31469                 }
31470                 
31471                 if(x > 0 && y > 0){
31472                     scale = targetWidth / width;
31473                     
31474                     if(width < height){
31475                         scale = targetHeight / height;
31476                     }
31477                 }
31478                 
31479                 context.scale(scale, scale);
31480                 
31481                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31482                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31483
31484                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31485                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31486
31487                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31488                 
31489                 break;
31490             case 90 : 
31491                 
31492                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31493                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31494                 
31495                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31496                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31497                 
31498                 var targetWidth = this.minWidth - 2 * x;
31499                 var targetHeight = this.minHeight - 2 * y;
31500                 
31501                 var scale = 1;
31502                 
31503                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31504                     scale = targetWidth / width;
31505                 }
31506                 
31507                 if(x > 0 && y == 0){
31508                     scale = targetHeight / height;
31509                 }
31510                 
31511                 if(x > 0 && y > 0){
31512                     scale = targetWidth / width;
31513                     
31514                     if(width < height){
31515                         scale = targetHeight / height;
31516                     }
31517                 }
31518                 
31519                 context.scale(scale, scale);
31520                 
31521                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31522                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31523
31524                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31525                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31526                 
31527                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31528                 
31529                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31530                 
31531                 break;
31532             case 180 :
31533                 
31534                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31535                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31536                 
31537                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31538                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31539                 
31540                 var targetWidth = this.minWidth - 2 * x;
31541                 var targetHeight = this.minHeight - 2 * y;
31542                 
31543                 var scale = 1;
31544                 
31545                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31546                     scale = targetWidth / width;
31547                 }
31548                 
31549                 if(x > 0 && y == 0){
31550                     scale = targetHeight / height;
31551                 }
31552                 
31553                 if(x > 0 && y > 0){
31554                     scale = targetWidth / width;
31555                     
31556                     if(width < height){
31557                         scale = targetHeight / height;
31558                     }
31559                 }
31560                 
31561                 context.scale(scale, scale);
31562                 
31563                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31564                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31565
31566                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31567                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31568
31569                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31570                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31571                 
31572                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31573                 
31574                 break;
31575             case 270 :
31576                 
31577                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31578                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31579                 
31580                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31581                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31582                 
31583                 var targetWidth = this.minWidth - 2 * x;
31584                 var targetHeight = this.minHeight - 2 * y;
31585                 
31586                 var scale = 1;
31587                 
31588                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31589                     scale = targetWidth / width;
31590                 }
31591                 
31592                 if(x > 0 && y == 0){
31593                     scale = targetHeight / height;
31594                 }
31595                 
31596                 if(x > 0 && y > 0){
31597                     scale = targetWidth / width;
31598                     
31599                     if(width < height){
31600                         scale = targetHeight / height;
31601                     }
31602                 }
31603                 
31604                 context.scale(scale, scale);
31605                 
31606                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31607                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31608
31609                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31610                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31611                 
31612                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31613                 
31614                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31615                 
31616                 break;
31617             default : 
31618                 break;
31619         }
31620         
31621         this.cropData = canvas.toDataURL(this.cropType);
31622         
31623         if(this.fireEvent('crop', this, this.cropData) !== false){
31624             this.process(this.file, this.cropData);
31625         }
31626         
31627         return;
31628         
31629     },
31630     
31631     setThumbBoxSize : function()
31632     {
31633         var width, height;
31634         
31635         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31636             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31637             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31638             
31639             this.minWidth = width;
31640             this.minHeight = height;
31641             
31642             if(this.rotate == 90 || this.rotate == 270){
31643                 this.minWidth = height;
31644                 this.minHeight = width;
31645             }
31646         }
31647         
31648         height = 300;
31649         width = Math.ceil(this.minWidth * height / this.minHeight);
31650         
31651         if(this.minWidth > this.minHeight){
31652             width = 300;
31653             height = Math.ceil(this.minHeight * width / this.minWidth);
31654         }
31655         
31656         this.thumbEl.setStyle({
31657             width : width + 'px',
31658             height : height + 'px'
31659         });
31660
31661         return;
31662             
31663     },
31664     
31665     setThumbBoxPosition : function()
31666     {
31667         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31668         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31669         
31670         this.thumbEl.setLeft(x);
31671         this.thumbEl.setTop(y);
31672         
31673     },
31674     
31675     baseRotateLevel : function()
31676     {
31677         this.baseRotate = 1;
31678         
31679         if(
31680                 typeof(this.exif) != 'undefined' &&
31681                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31682                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31683         ){
31684             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31685         }
31686         
31687         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31688         
31689     },
31690     
31691     baseScaleLevel : function()
31692     {
31693         var width, height;
31694         
31695         if(this.isDocument){
31696             
31697             if(this.baseRotate == 6 || this.baseRotate == 8){
31698             
31699                 height = this.thumbEl.getHeight();
31700                 this.baseScale = height / this.imageEl.OriginWidth;
31701
31702                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31703                     width = this.thumbEl.getWidth();
31704                     this.baseScale = width / this.imageEl.OriginHeight;
31705                 }
31706
31707                 return;
31708             }
31709
31710             height = this.thumbEl.getHeight();
31711             this.baseScale = height / this.imageEl.OriginHeight;
31712
31713             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31714                 width = this.thumbEl.getWidth();
31715                 this.baseScale = width / this.imageEl.OriginWidth;
31716             }
31717
31718             return;
31719         }
31720         
31721         if(this.baseRotate == 6 || this.baseRotate == 8){
31722             
31723             width = this.thumbEl.getHeight();
31724             this.baseScale = width / this.imageEl.OriginHeight;
31725             
31726             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31727                 height = this.thumbEl.getWidth();
31728                 this.baseScale = height / this.imageEl.OriginHeight;
31729             }
31730             
31731             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31732                 height = this.thumbEl.getWidth();
31733                 this.baseScale = height / this.imageEl.OriginHeight;
31734                 
31735                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31736                     width = this.thumbEl.getHeight();
31737                     this.baseScale = width / this.imageEl.OriginWidth;
31738                 }
31739             }
31740             
31741             return;
31742         }
31743         
31744         width = this.thumbEl.getWidth();
31745         this.baseScale = width / this.imageEl.OriginWidth;
31746         
31747         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31748             height = this.thumbEl.getHeight();
31749             this.baseScale = height / this.imageEl.OriginHeight;
31750         }
31751         
31752         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31753             
31754             height = this.thumbEl.getHeight();
31755             this.baseScale = height / this.imageEl.OriginHeight;
31756             
31757             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31758                 width = this.thumbEl.getWidth();
31759                 this.baseScale = width / this.imageEl.OriginWidth;
31760             }
31761             
31762         }
31763         
31764         return;
31765     },
31766     
31767     getScaleLevel : function()
31768     {
31769         return this.baseScale * Math.pow(1.1, this.scale);
31770     },
31771     
31772     onTouchStart : function(e)
31773     {
31774         if(!this.canvasLoaded){
31775             this.beforeSelectFile(e);
31776             return;
31777         }
31778         
31779         var touches = e.browserEvent.touches;
31780         
31781         if(!touches){
31782             return;
31783         }
31784         
31785         if(touches.length == 1){
31786             this.onMouseDown(e);
31787             return;
31788         }
31789         
31790         if(touches.length != 2){
31791             return;
31792         }
31793         
31794         var coords = [];
31795         
31796         for(var i = 0, finger; finger = touches[i]; i++){
31797             coords.push(finger.pageX, finger.pageY);
31798         }
31799         
31800         var x = Math.pow(coords[0] - coords[2], 2);
31801         var y = Math.pow(coords[1] - coords[3], 2);
31802         
31803         this.startDistance = Math.sqrt(x + y);
31804         
31805         this.startScale = this.scale;
31806         
31807         this.pinching = true;
31808         this.dragable = false;
31809         
31810     },
31811     
31812     onTouchMove : function(e)
31813     {
31814         if(!this.pinching && !this.dragable){
31815             return;
31816         }
31817         
31818         var touches = e.browserEvent.touches;
31819         
31820         if(!touches){
31821             return;
31822         }
31823         
31824         if(this.dragable){
31825             this.onMouseMove(e);
31826             return;
31827         }
31828         
31829         var coords = [];
31830         
31831         for(var i = 0, finger; finger = touches[i]; i++){
31832             coords.push(finger.pageX, finger.pageY);
31833         }
31834         
31835         var x = Math.pow(coords[0] - coords[2], 2);
31836         var y = Math.pow(coords[1] - coords[3], 2);
31837         
31838         this.endDistance = Math.sqrt(x + y);
31839         
31840         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31841         
31842         if(!this.zoomable()){
31843             this.scale = this.startScale;
31844             return;
31845         }
31846         
31847         this.draw();
31848         
31849     },
31850     
31851     onTouchEnd : function(e)
31852     {
31853         this.pinching = false;
31854         this.dragable = false;
31855         
31856     },
31857     
31858     process : function(file, crop)
31859     {
31860         if(this.loadMask){
31861             this.maskEl.mask(this.loadingText);
31862         }
31863         
31864         this.xhr = new XMLHttpRequest();
31865         
31866         file.xhr = this.xhr;
31867
31868         this.xhr.open(this.method, this.url, true);
31869         
31870         var headers = {
31871             "Accept": "application/json",
31872             "Cache-Control": "no-cache",
31873             "X-Requested-With": "XMLHttpRequest"
31874         };
31875         
31876         for (var headerName in headers) {
31877             var headerValue = headers[headerName];
31878             if (headerValue) {
31879                 this.xhr.setRequestHeader(headerName, headerValue);
31880             }
31881         }
31882         
31883         var _this = this;
31884         
31885         this.xhr.onload = function()
31886         {
31887             _this.xhrOnLoad(_this.xhr);
31888         }
31889         
31890         this.xhr.onerror = function()
31891         {
31892             _this.xhrOnError(_this.xhr);
31893         }
31894         
31895         var formData = new FormData();
31896
31897         formData.append('returnHTML', 'NO');
31898         
31899         if(crop){
31900             formData.append('crop', crop);
31901         }
31902         
31903         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31904             formData.append(this.paramName, file, file.name);
31905         }
31906         
31907         if(typeof(file.filename) != 'undefined'){
31908             formData.append('filename', file.filename);
31909         }
31910         
31911         if(typeof(file.mimetype) != 'undefined'){
31912             formData.append('mimetype', file.mimetype);
31913         }
31914         
31915         if(this.fireEvent('arrange', this, formData) != false){
31916             this.xhr.send(formData);
31917         };
31918     },
31919     
31920     xhrOnLoad : function(xhr)
31921     {
31922         if(this.loadMask){
31923             this.maskEl.unmask();
31924         }
31925         
31926         if (xhr.readyState !== 4) {
31927             this.fireEvent('exception', this, xhr);
31928             return;
31929         }
31930
31931         var response = Roo.decode(xhr.responseText);
31932         
31933         if(!response.success){
31934             this.fireEvent('exception', this, xhr);
31935             return;
31936         }
31937         
31938         var response = Roo.decode(xhr.responseText);
31939         
31940         this.fireEvent('upload', this, response);
31941         
31942     },
31943     
31944     xhrOnError : function()
31945     {
31946         if(this.loadMask){
31947             this.maskEl.unmask();
31948         }
31949         
31950         Roo.log('xhr on error');
31951         
31952         var response = Roo.decode(xhr.responseText);
31953           
31954         Roo.log(response);
31955         
31956     },
31957     
31958     prepare : function(file)
31959     {   
31960         if(this.loadMask){
31961             this.maskEl.mask(this.loadingText);
31962         }
31963         
31964         this.file = false;
31965         this.exif = {};
31966         
31967         if(typeof(file) === 'string'){
31968             this.loadCanvas(file);
31969             return;
31970         }
31971         
31972         if(!file || !this.urlAPI){
31973             return;
31974         }
31975         
31976         this.file = file;
31977         this.cropType = file.type;
31978         
31979         var _this = this;
31980         
31981         if(this.fireEvent('prepare', this, this.file) != false){
31982             
31983             var reader = new FileReader();
31984             
31985             reader.onload = function (e) {
31986                 if (e.target.error) {
31987                     Roo.log(e.target.error);
31988                     return;
31989                 }
31990                 
31991                 var buffer = e.target.result,
31992                     dataView = new DataView(buffer),
31993                     offset = 2,
31994                     maxOffset = dataView.byteLength - 4,
31995                     markerBytes,
31996                     markerLength;
31997                 
31998                 if (dataView.getUint16(0) === 0xffd8) {
31999                     while (offset < maxOffset) {
32000                         markerBytes = dataView.getUint16(offset);
32001                         
32002                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
32003                             markerLength = dataView.getUint16(offset + 2) + 2;
32004                             if (offset + markerLength > dataView.byteLength) {
32005                                 Roo.log('Invalid meta data: Invalid segment size.');
32006                                 break;
32007                             }
32008                             
32009                             if(markerBytes == 0xffe1){
32010                                 _this.parseExifData(
32011                                     dataView,
32012                                     offset,
32013                                     markerLength
32014                                 );
32015                             }
32016                             
32017                             offset += markerLength;
32018                             
32019                             continue;
32020                         }
32021                         
32022                         break;
32023                     }
32024                     
32025                 }
32026                 
32027                 var url = _this.urlAPI.createObjectURL(_this.file);
32028                 
32029                 _this.loadCanvas(url);
32030                 
32031                 return;
32032             }
32033             
32034             reader.readAsArrayBuffer(this.file);
32035             
32036         }
32037         
32038     },
32039     
32040     parseExifData : function(dataView, offset, length)
32041     {
32042         var tiffOffset = offset + 10,
32043             littleEndian,
32044             dirOffset;
32045     
32046         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32047             // No Exif data, might be XMP data instead
32048             return;
32049         }
32050         
32051         // Check for the ASCII code for "Exif" (0x45786966):
32052         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32053             // No Exif data, might be XMP data instead
32054             return;
32055         }
32056         if (tiffOffset + 8 > dataView.byteLength) {
32057             Roo.log('Invalid Exif data: Invalid segment size.');
32058             return;
32059         }
32060         // Check for the two null bytes:
32061         if (dataView.getUint16(offset + 8) !== 0x0000) {
32062             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32063             return;
32064         }
32065         // Check the byte alignment:
32066         switch (dataView.getUint16(tiffOffset)) {
32067         case 0x4949:
32068             littleEndian = true;
32069             break;
32070         case 0x4D4D:
32071             littleEndian = false;
32072             break;
32073         default:
32074             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32075             return;
32076         }
32077         // Check for the TIFF tag marker (0x002A):
32078         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32079             Roo.log('Invalid Exif data: Missing TIFF marker.');
32080             return;
32081         }
32082         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32083         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32084         
32085         this.parseExifTags(
32086             dataView,
32087             tiffOffset,
32088             tiffOffset + dirOffset,
32089             littleEndian
32090         );
32091     },
32092     
32093     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32094     {
32095         var tagsNumber,
32096             dirEndOffset,
32097             i;
32098         if (dirOffset + 6 > dataView.byteLength) {
32099             Roo.log('Invalid Exif data: Invalid directory offset.');
32100             return;
32101         }
32102         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32103         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32104         if (dirEndOffset + 4 > dataView.byteLength) {
32105             Roo.log('Invalid Exif data: Invalid directory size.');
32106             return;
32107         }
32108         for (i = 0; i < tagsNumber; i += 1) {
32109             this.parseExifTag(
32110                 dataView,
32111                 tiffOffset,
32112                 dirOffset + 2 + 12 * i, // tag offset
32113                 littleEndian
32114             );
32115         }
32116         // Return the offset to the next directory:
32117         return dataView.getUint32(dirEndOffset, littleEndian);
32118     },
32119     
32120     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32121     {
32122         var tag = dataView.getUint16(offset, littleEndian);
32123         
32124         this.exif[tag] = this.getExifValue(
32125             dataView,
32126             tiffOffset,
32127             offset,
32128             dataView.getUint16(offset + 2, littleEndian), // tag type
32129             dataView.getUint32(offset + 4, littleEndian), // tag length
32130             littleEndian
32131         );
32132     },
32133     
32134     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32135     {
32136         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32137             tagSize,
32138             dataOffset,
32139             values,
32140             i,
32141             str,
32142             c;
32143     
32144         if (!tagType) {
32145             Roo.log('Invalid Exif data: Invalid tag type.');
32146             return;
32147         }
32148         
32149         tagSize = tagType.size * length;
32150         // Determine if the value is contained in the dataOffset bytes,
32151         // or if the value at the dataOffset is a pointer to the actual data:
32152         dataOffset = tagSize > 4 ?
32153                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32154         if (dataOffset + tagSize > dataView.byteLength) {
32155             Roo.log('Invalid Exif data: Invalid data offset.');
32156             return;
32157         }
32158         if (length === 1) {
32159             return tagType.getValue(dataView, dataOffset, littleEndian);
32160         }
32161         values = [];
32162         for (i = 0; i < length; i += 1) {
32163             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32164         }
32165         
32166         if (tagType.ascii) {
32167             str = '';
32168             // Concatenate the chars:
32169             for (i = 0; i < values.length; i += 1) {
32170                 c = values[i];
32171                 // Ignore the terminating NULL byte(s):
32172                 if (c === '\u0000') {
32173                     break;
32174                 }
32175                 str += c;
32176             }
32177             return str;
32178         }
32179         return values;
32180     }
32181     
32182 });
32183
32184 Roo.apply(Roo.bootstrap.UploadCropbox, {
32185     tags : {
32186         'Orientation': 0x0112
32187     },
32188     
32189     Orientation: {
32190             1: 0, //'top-left',
32191 //            2: 'top-right',
32192             3: 180, //'bottom-right',
32193 //            4: 'bottom-left',
32194 //            5: 'left-top',
32195             6: 90, //'right-top',
32196 //            7: 'right-bottom',
32197             8: 270 //'left-bottom'
32198     },
32199     
32200     exifTagTypes : {
32201         // byte, 8-bit unsigned int:
32202         1: {
32203             getValue: function (dataView, dataOffset) {
32204                 return dataView.getUint8(dataOffset);
32205             },
32206             size: 1
32207         },
32208         // ascii, 8-bit byte:
32209         2: {
32210             getValue: function (dataView, dataOffset) {
32211                 return String.fromCharCode(dataView.getUint8(dataOffset));
32212             },
32213             size: 1,
32214             ascii: true
32215         },
32216         // short, 16 bit int:
32217         3: {
32218             getValue: function (dataView, dataOffset, littleEndian) {
32219                 return dataView.getUint16(dataOffset, littleEndian);
32220             },
32221             size: 2
32222         },
32223         // long, 32 bit int:
32224         4: {
32225             getValue: function (dataView, dataOffset, littleEndian) {
32226                 return dataView.getUint32(dataOffset, littleEndian);
32227             },
32228             size: 4
32229         },
32230         // rational = two long values, first is numerator, second is denominator:
32231         5: {
32232             getValue: function (dataView, dataOffset, littleEndian) {
32233                 return dataView.getUint32(dataOffset, littleEndian) /
32234                     dataView.getUint32(dataOffset + 4, littleEndian);
32235             },
32236             size: 8
32237         },
32238         // slong, 32 bit signed int:
32239         9: {
32240             getValue: function (dataView, dataOffset, littleEndian) {
32241                 return dataView.getInt32(dataOffset, littleEndian);
32242             },
32243             size: 4
32244         },
32245         // srational, two slongs, first is numerator, second is denominator:
32246         10: {
32247             getValue: function (dataView, dataOffset, littleEndian) {
32248                 return dataView.getInt32(dataOffset, littleEndian) /
32249                     dataView.getInt32(dataOffset + 4, littleEndian);
32250             },
32251             size: 8
32252         }
32253     },
32254     
32255     footer : {
32256         STANDARD : [
32257             {
32258                 tag : 'div',
32259                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32260                 action : 'rotate-left',
32261                 cn : [
32262                     {
32263                         tag : 'button',
32264                         cls : 'btn btn-default',
32265                         html : '<i class="fa fa-undo"></i>'
32266                     }
32267                 ]
32268             },
32269             {
32270                 tag : 'div',
32271                 cls : 'btn-group roo-upload-cropbox-picture',
32272                 action : 'picture',
32273                 cn : [
32274                     {
32275                         tag : 'button',
32276                         cls : 'btn btn-default',
32277                         html : '<i class="fa fa-picture-o"></i>'
32278                     }
32279                 ]
32280             },
32281             {
32282                 tag : 'div',
32283                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32284                 action : 'rotate-right',
32285                 cn : [
32286                     {
32287                         tag : 'button',
32288                         cls : 'btn btn-default',
32289                         html : '<i class="fa fa-repeat"></i>'
32290                     }
32291                 ]
32292             }
32293         ],
32294         DOCUMENT : [
32295             {
32296                 tag : 'div',
32297                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32298                 action : 'rotate-left',
32299                 cn : [
32300                     {
32301                         tag : 'button',
32302                         cls : 'btn btn-default',
32303                         html : '<i class="fa fa-undo"></i>'
32304                     }
32305                 ]
32306             },
32307             {
32308                 tag : 'div',
32309                 cls : 'btn-group roo-upload-cropbox-download',
32310                 action : 'download',
32311                 cn : [
32312                     {
32313                         tag : 'button',
32314                         cls : 'btn btn-default',
32315                         html : '<i class="fa fa-download"></i>'
32316                     }
32317                 ]
32318             },
32319             {
32320                 tag : 'div',
32321                 cls : 'btn-group roo-upload-cropbox-crop',
32322                 action : 'crop',
32323                 cn : [
32324                     {
32325                         tag : 'button',
32326                         cls : 'btn btn-default',
32327                         html : '<i class="fa fa-crop"></i>'
32328                     }
32329                 ]
32330             },
32331             {
32332                 tag : 'div',
32333                 cls : 'btn-group roo-upload-cropbox-trash',
32334                 action : 'trash',
32335                 cn : [
32336                     {
32337                         tag : 'button',
32338                         cls : 'btn btn-default',
32339                         html : '<i class="fa fa-trash"></i>'
32340                     }
32341                 ]
32342             },
32343             {
32344                 tag : 'div',
32345                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32346                 action : 'rotate-right',
32347                 cn : [
32348                     {
32349                         tag : 'button',
32350                         cls : 'btn btn-default',
32351                         html : '<i class="fa fa-repeat"></i>'
32352                     }
32353                 ]
32354             }
32355         ],
32356         ROTATOR : [
32357             {
32358                 tag : 'div',
32359                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32360                 action : 'rotate-left',
32361                 cn : [
32362                     {
32363                         tag : 'button',
32364                         cls : 'btn btn-default',
32365                         html : '<i class="fa fa-undo"></i>'
32366                     }
32367                 ]
32368             },
32369             {
32370                 tag : 'div',
32371                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32372                 action : 'rotate-right',
32373                 cn : [
32374                     {
32375                         tag : 'button',
32376                         cls : 'btn btn-default',
32377                         html : '<i class="fa fa-repeat"></i>'
32378                     }
32379                 ]
32380             }
32381         ]
32382     }
32383 });
32384
32385 /*
32386 * Licence: LGPL
32387 */
32388
32389 /**
32390  * @class Roo.bootstrap.DocumentManager
32391  * @extends Roo.bootstrap.Component
32392  * Bootstrap DocumentManager class
32393  * @cfg {String} paramName default 'imageUpload'
32394  * @cfg {String} toolTipName default 'filename'
32395  * @cfg {String} method default POST
32396  * @cfg {String} url action url
32397  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32398  * @cfg {Boolean} multiple multiple upload default true
32399  * @cfg {Number} thumbSize default 300
32400  * @cfg {String} fieldLabel
32401  * @cfg {Number} labelWidth default 4
32402  * @cfg {String} labelAlign (left|top) default left
32403  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32404 * @cfg {Number} labellg set the width of label (1-12)
32405  * @cfg {Number} labelmd set the width of label (1-12)
32406  * @cfg {Number} labelsm set the width of label (1-12)
32407  * @cfg {Number} labelxs set the width of label (1-12)
32408  * 
32409  * @constructor
32410  * Create a new DocumentManager
32411  * @param {Object} config The config object
32412  */
32413
32414 Roo.bootstrap.DocumentManager = function(config){
32415     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32416     
32417     this.files = [];
32418     this.delegates = [];
32419     
32420     this.addEvents({
32421         /**
32422          * @event initial
32423          * Fire when initial the DocumentManager
32424          * @param {Roo.bootstrap.DocumentManager} this
32425          */
32426         "initial" : true,
32427         /**
32428          * @event inspect
32429          * inspect selected file
32430          * @param {Roo.bootstrap.DocumentManager} this
32431          * @param {File} file
32432          */
32433         "inspect" : true,
32434         /**
32435          * @event exception
32436          * Fire when xhr load exception
32437          * @param {Roo.bootstrap.DocumentManager} this
32438          * @param {XMLHttpRequest} xhr
32439          */
32440         "exception" : true,
32441         /**
32442          * @event afterupload
32443          * Fire when xhr load exception
32444          * @param {Roo.bootstrap.DocumentManager} this
32445          * @param {XMLHttpRequest} xhr
32446          */
32447         "afterupload" : true,
32448         /**
32449          * @event prepare
32450          * prepare the form data
32451          * @param {Roo.bootstrap.DocumentManager} this
32452          * @param {Object} formData
32453          */
32454         "prepare" : true,
32455         /**
32456          * @event remove
32457          * Fire when remove the file
32458          * @param {Roo.bootstrap.DocumentManager} this
32459          * @param {Object} file
32460          */
32461         "remove" : true,
32462         /**
32463          * @event refresh
32464          * Fire after refresh the file
32465          * @param {Roo.bootstrap.DocumentManager} this
32466          */
32467         "refresh" : true,
32468         /**
32469          * @event click
32470          * Fire after click the image
32471          * @param {Roo.bootstrap.DocumentManager} this
32472          * @param {Object} file
32473          */
32474         "click" : true,
32475         /**
32476          * @event edit
32477          * Fire when upload a image and editable set to true
32478          * @param {Roo.bootstrap.DocumentManager} this
32479          * @param {Object} file
32480          */
32481         "edit" : true,
32482         /**
32483          * @event beforeselectfile
32484          * Fire before select file
32485          * @param {Roo.bootstrap.DocumentManager} this
32486          */
32487         "beforeselectfile" : true,
32488         /**
32489          * @event process
32490          * Fire before process file
32491          * @param {Roo.bootstrap.DocumentManager} this
32492          * @param {Object} file
32493          */
32494         "process" : true,
32495         /**
32496          * @event previewrendered
32497          * Fire when preview rendered
32498          * @param {Roo.bootstrap.DocumentManager} this
32499          * @param {Object} file
32500          */
32501         "previewrendered" : true,
32502         /**
32503          */
32504         "previewResize" : true
32505         
32506     });
32507 };
32508
32509 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32510     
32511     boxes : 0,
32512     inputName : '',
32513     thumbSize : 300,
32514     multiple : true,
32515     files : false,
32516     method : 'POST',
32517     url : '',
32518     paramName : 'imageUpload',
32519     toolTipName : 'filename',
32520     fieldLabel : '',
32521     labelWidth : 4,
32522     labelAlign : 'left',
32523     editable : true,
32524     delegates : false,
32525     xhr : false, 
32526     
32527     labellg : 0,
32528     labelmd : 0,
32529     labelsm : 0,
32530     labelxs : 0,
32531     
32532     getAutoCreate : function()
32533     {   
32534         var managerWidget = {
32535             tag : 'div',
32536             cls : 'roo-document-manager',
32537             cn : [
32538                 {
32539                     tag : 'input',
32540                     cls : 'roo-document-manager-selector',
32541                     type : 'file'
32542                 },
32543                 {
32544                     tag : 'div',
32545                     cls : 'roo-document-manager-uploader',
32546                     cn : [
32547                         {
32548                             tag : 'div',
32549                             cls : 'roo-document-manager-upload-btn',
32550                             html : '<i class="fa fa-plus"></i>'
32551                         }
32552                     ]
32553                     
32554                 }
32555             ]
32556         };
32557         
32558         var content = [
32559             {
32560                 tag : 'div',
32561                 cls : 'column col-md-12',
32562                 cn : managerWidget
32563             }
32564         ];
32565         
32566         if(this.fieldLabel.length){
32567             
32568             content = [
32569                 {
32570                     tag : 'div',
32571                     cls : 'column col-md-12',
32572                     html : this.fieldLabel
32573                 },
32574                 {
32575                     tag : 'div',
32576                     cls : 'column col-md-12',
32577                     cn : managerWidget
32578                 }
32579             ];
32580
32581             if(this.labelAlign == 'left'){
32582                 content = [
32583                     {
32584                         tag : 'div',
32585                         cls : 'column',
32586                         html : this.fieldLabel
32587                     },
32588                     {
32589                         tag : 'div',
32590                         cls : 'column',
32591                         cn : managerWidget
32592                     }
32593                 ];
32594                 
32595                 if(this.labelWidth > 12){
32596                     content[0].style = "width: " + this.labelWidth + 'px';
32597                 }
32598
32599                 if(this.labelWidth < 13 && this.labelmd == 0){
32600                     this.labelmd = this.labelWidth;
32601                 }
32602
32603                 if(this.labellg > 0){
32604                     content[0].cls += ' col-lg-' + this.labellg;
32605                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32606                 }
32607
32608                 if(this.labelmd > 0){
32609                     content[0].cls += ' col-md-' + this.labelmd;
32610                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32611                 }
32612
32613                 if(this.labelsm > 0){
32614                     content[0].cls += ' col-sm-' + this.labelsm;
32615                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32616                 }
32617
32618                 if(this.labelxs > 0){
32619                     content[0].cls += ' col-xs-' + this.labelxs;
32620                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32621                 }
32622                 
32623             }
32624         }
32625         
32626         var cfg = {
32627             tag : 'div',
32628             cls : 'row clearfix',
32629             cn : content
32630         };
32631         
32632         return cfg;
32633         
32634     },
32635     
32636     initEvents : function()
32637     {
32638         this.managerEl = this.el.select('.roo-document-manager', true).first();
32639         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32640         
32641         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32642         this.selectorEl.hide();
32643         
32644         if(this.multiple){
32645             this.selectorEl.attr('multiple', 'multiple');
32646         }
32647         
32648         this.selectorEl.on('change', this.onFileSelected, this);
32649         
32650         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32651         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32652         
32653         this.uploader.on('click', this.onUploaderClick, this);
32654         
32655         this.renderProgressDialog();
32656         
32657         var _this = this;
32658         
32659         window.addEventListener("resize", function() { _this.refresh(); } );
32660         
32661         this.fireEvent('initial', this);
32662     },
32663     
32664     renderProgressDialog : function()
32665     {
32666         var _this = this;
32667         
32668         this.progressDialog = new Roo.bootstrap.Modal({
32669             cls : 'roo-document-manager-progress-dialog',
32670             allow_close : false,
32671             animate : false,
32672             title : '',
32673             buttons : [
32674                 {
32675                     name  :'cancel',
32676                     weight : 'danger',
32677                     html : 'Cancel'
32678                 }
32679             ], 
32680             listeners : { 
32681                 btnclick : function() {
32682                     _this.uploadCancel();
32683                     this.hide();
32684                 }
32685             }
32686         });
32687          
32688         this.progressDialog.render(Roo.get(document.body));
32689          
32690         this.progress = new Roo.bootstrap.Progress({
32691             cls : 'roo-document-manager-progress',
32692             active : true,
32693             striped : true
32694         });
32695         
32696         this.progress.render(this.progressDialog.getChildContainer());
32697         
32698         this.progressBar = new Roo.bootstrap.ProgressBar({
32699             cls : 'roo-document-manager-progress-bar',
32700             aria_valuenow : 0,
32701             aria_valuemin : 0,
32702             aria_valuemax : 12,
32703             panel : 'success'
32704         });
32705         
32706         this.progressBar.render(this.progress.getChildContainer());
32707     },
32708     
32709     onUploaderClick : function(e)
32710     {
32711         e.preventDefault();
32712      
32713         if(this.fireEvent('beforeselectfile', this) != false){
32714             this.selectorEl.dom.click();
32715         }
32716         
32717     },
32718     
32719     onFileSelected : function(e)
32720     {
32721         e.preventDefault();
32722         
32723         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32724             return;
32725         }
32726         
32727         Roo.each(this.selectorEl.dom.files, function(file){
32728             if(this.fireEvent('inspect', this, file) != false){
32729                 this.files.push(file);
32730             }
32731         }, this);
32732         
32733         this.queue();
32734         
32735     },
32736     
32737     queue : function()
32738     {
32739         this.selectorEl.dom.value = '';
32740         
32741         if(!this.files || !this.files.length){
32742             return;
32743         }
32744         
32745         if(this.boxes > 0 && this.files.length > this.boxes){
32746             this.files = this.files.slice(0, this.boxes);
32747         }
32748         
32749         this.uploader.show();
32750         
32751         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32752             this.uploader.hide();
32753         }
32754         
32755         var _this = this;
32756         
32757         var files = [];
32758         
32759         var docs = [];
32760         
32761         Roo.each(this.files, function(file){
32762             
32763             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32764                 var f = this.renderPreview(file);
32765                 files.push(f);
32766                 return;
32767             }
32768             
32769             if(file.type.indexOf('image') != -1){
32770                 this.delegates.push(
32771                     (function(){
32772                         _this.process(file);
32773                     }).createDelegate(this)
32774                 );
32775         
32776                 return;
32777             }
32778             
32779             docs.push(
32780                 (function(){
32781                     _this.process(file);
32782                 }).createDelegate(this)
32783             );
32784             
32785         }, this);
32786         
32787         this.files = files;
32788         
32789         this.delegates = this.delegates.concat(docs);
32790         
32791         if(!this.delegates.length){
32792             this.refresh();
32793             return;
32794         }
32795         
32796         this.progressBar.aria_valuemax = this.delegates.length;
32797         
32798         this.arrange();
32799         
32800         return;
32801     },
32802     
32803     arrange : function()
32804     {
32805         if(!this.delegates.length){
32806             this.progressDialog.hide();
32807             this.refresh();
32808             return;
32809         }
32810         
32811         var delegate = this.delegates.shift();
32812         
32813         this.progressDialog.show();
32814         
32815         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32816         
32817         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32818         
32819         delegate();
32820     },
32821     
32822     refresh : function()
32823     {
32824         this.uploader.show();
32825         
32826         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32827             this.uploader.hide();
32828         }
32829         
32830         Roo.isTouch ? this.closable(false) : this.closable(true);
32831         
32832         this.fireEvent('refresh', this);
32833     },
32834     
32835     onRemove : function(e, el, o)
32836     {
32837         e.preventDefault();
32838         
32839         this.fireEvent('remove', this, o);
32840         
32841     },
32842     
32843     remove : function(o)
32844     {
32845         var files = [];
32846         
32847         Roo.each(this.files, function(file){
32848             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32849                 files.push(file);
32850                 return;
32851             }
32852
32853             o.target.remove();
32854
32855         }, this);
32856         
32857         this.files = files;
32858         
32859         this.refresh();
32860     },
32861     
32862     clear : function()
32863     {
32864         Roo.each(this.files, function(file){
32865             if(!file.target){
32866                 return;
32867             }
32868             
32869             file.target.remove();
32870
32871         }, this);
32872         
32873         this.files = [];
32874         
32875         this.refresh();
32876     },
32877     
32878     onClick : function(e, el, o)
32879     {
32880         e.preventDefault();
32881         
32882         this.fireEvent('click', this, o);
32883         
32884     },
32885     
32886     closable : function(closable)
32887     {
32888         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32889             
32890             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32891             
32892             if(closable){
32893                 el.show();
32894                 return;
32895             }
32896             
32897             el.hide();
32898             
32899         }, this);
32900     },
32901     
32902     xhrOnLoad : function(xhr)
32903     {
32904         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32905             el.remove();
32906         }, this);
32907         
32908         if (xhr.readyState !== 4) {
32909             this.arrange();
32910             this.fireEvent('exception', this, xhr);
32911             return;
32912         }
32913
32914         var response = Roo.decode(xhr.responseText);
32915         
32916         if(!response.success){
32917             this.arrange();
32918             this.fireEvent('exception', this, xhr);
32919             return;
32920         }
32921         
32922         var file = this.renderPreview(response.data);
32923         
32924         this.files.push(file);
32925         
32926         this.arrange();
32927         
32928         this.fireEvent('afterupload', this, xhr);
32929         
32930     },
32931     
32932     xhrOnError : function(xhr)
32933     {
32934         Roo.log('xhr on error');
32935         
32936         var response = Roo.decode(xhr.responseText);
32937           
32938         Roo.log(response);
32939         
32940         this.arrange();
32941     },
32942     
32943     process : function(file)
32944     {
32945         if(this.fireEvent('process', this, file) !== false){
32946             if(this.editable && file.type.indexOf('image') != -1){
32947                 this.fireEvent('edit', this, file);
32948                 return;
32949             }
32950
32951             this.uploadStart(file, false);
32952
32953             return;
32954         }
32955         
32956     },
32957     
32958     uploadStart : function(file, crop)
32959     {
32960         this.xhr = new XMLHttpRequest();
32961         
32962         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32963             this.arrange();
32964             return;
32965         }
32966         
32967         file.xhr = this.xhr;
32968             
32969         this.managerEl.createChild({
32970             tag : 'div',
32971             cls : 'roo-document-manager-loading',
32972             cn : [
32973                 {
32974                     tag : 'div',
32975                     tooltip : file.name,
32976                     cls : 'roo-document-manager-thumb',
32977                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32978                 }
32979             ]
32980
32981         });
32982
32983         this.xhr.open(this.method, this.url, true);
32984         
32985         var headers = {
32986             "Accept": "application/json",
32987             "Cache-Control": "no-cache",
32988             "X-Requested-With": "XMLHttpRequest"
32989         };
32990         
32991         for (var headerName in headers) {
32992             var headerValue = headers[headerName];
32993             if (headerValue) {
32994                 this.xhr.setRequestHeader(headerName, headerValue);
32995             }
32996         }
32997         
32998         var _this = this;
32999         
33000         this.xhr.onload = function()
33001         {
33002             _this.xhrOnLoad(_this.xhr);
33003         }
33004         
33005         this.xhr.onerror = function()
33006         {
33007             _this.xhrOnError(_this.xhr);
33008         }
33009         
33010         var formData = new FormData();
33011
33012         formData.append('returnHTML', 'NO');
33013         
33014         if(crop){
33015             formData.append('crop', crop);
33016         }
33017         
33018         formData.append(this.paramName, file, file.name);
33019         
33020         var options = {
33021             file : file, 
33022             manually : false
33023         };
33024         
33025         if(this.fireEvent('prepare', this, formData, options) != false){
33026             
33027             if(options.manually){
33028                 return;
33029             }
33030             
33031             this.xhr.send(formData);
33032             return;
33033         };
33034         
33035         this.uploadCancel();
33036     },
33037     
33038     uploadCancel : function()
33039     {
33040         if (this.xhr) {
33041             this.xhr.abort();
33042         }
33043         
33044         this.delegates = [];
33045         
33046         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33047             el.remove();
33048         }, this);
33049         
33050         this.arrange();
33051     },
33052     
33053     renderPreview : function(file)
33054     {
33055         if(typeof(file.target) != 'undefined' && file.target){
33056             return file;
33057         }
33058         
33059         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33060         
33061         var previewEl = this.managerEl.createChild({
33062             tag : 'div',
33063             cls : 'roo-document-manager-preview',
33064             cn : [
33065                 {
33066                     tag : 'div',
33067                     tooltip : file[this.toolTipName],
33068                     cls : 'roo-document-manager-thumb',
33069                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33070                 },
33071                 {
33072                     tag : 'button',
33073                     cls : 'close',
33074                     html : '<i class="fa fa-times-circle"></i>'
33075                 }
33076             ]
33077         });
33078
33079         var close = previewEl.select('button.close', true).first();
33080
33081         close.on('click', this.onRemove, this, file);
33082
33083         file.target = previewEl;
33084
33085         var image = previewEl.select('img', true).first();
33086         
33087         var _this = this;
33088         
33089         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33090         
33091         image.on('click', this.onClick, this, file);
33092         
33093         this.fireEvent('previewrendered', this, file);
33094         
33095         return file;
33096         
33097     },
33098     
33099     onPreviewLoad : function(file, image)
33100     {
33101         if(typeof(file.target) == 'undefined' || !file.target){
33102             return;
33103         }
33104         
33105         var width = image.dom.naturalWidth || image.dom.width;
33106         var height = image.dom.naturalHeight || image.dom.height;
33107         
33108         if(!this.previewResize) {
33109             return;
33110         }
33111         
33112         if(width > height){
33113             file.target.addClass('wide');
33114             return;
33115         }
33116         
33117         file.target.addClass('tall');
33118         return;
33119         
33120     },
33121     
33122     uploadFromSource : function(file, crop)
33123     {
33124         this.xhr = new XMLHttpRequest();
33125         
33126         this.managerEl.createChild({
33127             tag : 'div',
33128             cls : 'roo-document-manager-loading',
33129             cn : [
33130                 {
33131                     tag : 'div',
33132                     tooltip : file.name,
33133                     cls : 'roo-document-manager-thumb',
33134                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33135                 }
33136             ]
33137
33138         });
33139
33140         this.xhr.open(this.method, this.url, true);
33141         
33142         var headers = {
33143             "Accept": "application/json",
33144             "Cache-Control": "no-cache",
33145             "X-Requested-With": "XMLHttpRequest"
33146         };
33147         
33148         for (var headerName in headers) {
33149             var headerValue = headers[headerName];
33150             if (headerValue) {
33151                 this.xhr.setRequestHeader(headerName, headerValue);
33152             }
33153         }
33154         
33155         var _this = this;
33156         
33157         this.xhr.onload = function()
33158         {
33159             _this.xhrOnLoad(_this.xhr);
33160         }
33161         
33162         this.xhr.onerror = function()
33163         {
33164             _this.xhrOnError(_this.xhr);
33165         }
33166         
33167         var formData = new FormData();
33168
33169         formData.append('returnHTML', 'NO');
33170         
33171         formData.append('crop', crop);
33172         
33173         if(typeof(file.filename) != 'undefined'){
33174             formData.append('filename', file.filename);
33175         }
33176         
33177         if(typeof(file.mimetype) != 'undefined'){
33178             formData.append('mimetype', file.mimetype);
33179         }
33180         
33181         Roo.log(formData);
33182         
33183         if(this.fireEvent('prepare', this, formData) != false){
33184             this.xhr.send(formData);
33185         };
33186     }
33187 });
33188
33189 /*
33190 * Licence: LGPL
33191 */
33192
33193 /**
33194  * @class Roo.bootstrap.DocumentViewer
33195  * @extends Roo.bootstrap.Component
33196  * Bootstrap DocumentViewer class
33197  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33198  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33199  * 
33200  * @constructor
33201  * Create a new DocumentViewer
33202  * @param {Object} config The config object
33203  */
33204
33205 Roo.bootstrap.DocumentViewer = function(config){
33206     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33207     
33208     this.addEvents({
33209         /**
33210          * @event initial
33211          * Fire after initEvent
33212          * @param {Roo.bootstrap.DocumentViewer} this
33213          */
33214         "initial" : true,
33215         /**
33216          * @event click
33217          * Fire after click
33218          * @param {Roo.bootstrap.DocumentViewer} this
33219          */
33220         "click" : true,
33221         /**
33222          * @event download
33223          * Fire after download button
33224          * @param {Roo.bootstrap.DocumentViewer} this
33225          */
33226         "download" : true,
33227         /**
33228          * @event trash
33229          * Fire after trash button
33230          * @param {Roo.bootstrap.DocumentViewer} this
33231          */
33232         "trash" : true
33233         
33234     });
33235 };
33236
33237 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33238     
33239     showDownload : true,
33240     
33241     showTrash : true,
33242     
33243     getAutoCreate : function()
33244     {
33245         var cfg = {
33246             tag : 'div',
33247             cls : 'roo-document-viewer',
33248             cn : [
33249                 {
33250                     tag : 'div',
33251                     cls : 'roo-document-viewer-body',
33252                     cn : [
33253                         {
33254                             tag : 'div',
33255                             cls : 'roo-document-viewer-thumb',
33256                             cn : [
33257                                 {
33258                                     tag : 'img',
33259                                     cls : 'roo-document-viewer-image'
33260                                 }
33261                             ]
33262                         }
33263                     ]
33264                 },
33265                 {
33266                     tag : 'div',
33267                     cls : 'roo-document-viewer-footer',
33268                     cn : {
33269                         tag : 'div',
33270                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33271                         cn : [
33272                             {
33273                                 tag : 'div',
33274                                 cls : 'btn-group roo-document-viewer-download',
33275                                 cn : [
33276                                     {
33277                                         tag : 'button',
33278                                         cls : 'btn btn-default',
33279                                         html : '<i class="fa fa-download"></i>'
33280                                     }
33281                                 ]
33282                             },
33283                             {
33284                                 tag : 'div',
33285                                 cls : 'btn-group roo-document-viewer-trash',
33286                                 cn : [
33287                                     {
33288                                         tag : 'button',
33289                                         cls : 'btn btn-default',
33290                                         html : '<i class="fa fa-trash"></i>'
33291                                     }
33292                                 ]
33293                             }
33294                         ]
33295                     }
33296                 }
33297             ]
33298         };
33299         
33300         return cfg;
33301     },
33302     
33303     initEvents : function()
33304     {
33305         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33306         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33307         
33308         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33309         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33310         
33311         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33312         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33313         
33314         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33315         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33316         
33317         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33318         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33319         
33320         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33321         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33322         
33323         this.bodyEl.on('click', this.onClick, this);
33324         this.downloadBtn.on('click', this.onDownload, this);
33325         this.trashBtn.on('click', this.onTrash, this);
33326         
33327         this.downloadBtn.hide();
33328         this.trashBtn.hide();
33329         
33330         if(this.showDownload){
33331             this.downloadBtn.show();
33332         }
33333         
33334         if(this.showTrash){
33335             this.trashBtn.show();
33336         }
33337         
33338         if(!this.showDownload && !this.showTrash) {
33339             this.footerEl.hide();
33340         }
33341         
33342     },
33343     
33344     initial : function()
33345     {
33346         this.fireEvent('initial', this);
33347         
33348     },
33349     
33350     onClick : function(e)
33351     {
33352         e.preventDefault();
33353         
33354         this.fireEvent('click', this);
33355     },
33356     
33357     onDownload : function(e)
33358     {
33359         e.preventDefault();
33360         
33361         this.fireEvent('download', this);
33362     },
33363     
33364     onTrash : function(e)
33365     {
33366         e.preventDefault();
33367         
33368         this.fireEvent('trash', this);
33369     }
33370     
33371 });
33372 /*
33373  * - LGPL
33374  *
33375  * FieldLabel
33376  * 
33377  */
33378
33379 /**
33380  * @class Roo.bootstrap.form.FieldLabel
33381  * @extends Roo.bootstrap.Component
33382  * Bootstrap FieldLabel class
33383  * @cfg {String} html contents of the element
33384  * @cfg {String} tag tag of the element default label
33385  * @cfg {String} cls class of the element
33386  * @cfg {String} target label target 
33387  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33388  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33389  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33390  * @cfg {String} iconTooltip default "This field is required"
33391  * @cfg {String} indicatorpos (left|right) default left
33392  * 
33393  * @constructor
33394  * Create a new FieldLabel
33395  * @param {Object} config The config object
33396  */
33397
33398 Roo.bootstrap.form.FieldLabel = function(config){
33399     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33400     
33401     this.addEvents({
33402             /**
33403              * @event invalid
33404              * Fires after the field has been marked as invalid.
33405              * @param {Roo.form.FieldLabel} this
33406              * @param {String} msg The validation message
33407              */
33408             invalid : true,
33409             /**
33410              * @event valid
33411              * Fires after the field has been validated with no errors.
33412              * @param {Roo.form.FieldLabel} this
33413              */
33414             valid : true
33415         });
33416 };
33417
33418 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
33419     
33420     tag: 'label',
33421     cls: '',
33422     html: '',
33423     target: '',
33424     allowBlank : true,
33425     invalidClass : 'has-warning',
33426     validClass : 'has-success',
33427     iconTooltip : 'This field is required',
33428     indicatorpos : 'left',
33429     
33430     getAutoCreate : function(){
33431         
33432         var cls = "";
33433         if (!this.allowBlank) {
33434             cls  = "visible";
33435         }
33436         
33437         var cfg = {
33438             tag : this.tag,
33439             cls : 'roo-bootstrap-field-label ' + this.cls,
33440             for : this.target,
33441             cn : [
33442                 {
33443                     tag : 'i',
33444                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33445                     tooltip : this.iconTooltip
33446                 },
33447                 {
33448                     tag : 'span',
33449                     html : this.html
33450                 }
33451             ] 
33452         };
33453         
33454         if(this.indicatorpos == 'right'){
33455             var cfg = {
33456                 tag : this.tag,
33457                 cls : 'roo-bootstrap-field-label ' + this.cls,
33458                 for : this.target,
33459                 cn : [
33460                     {
33461                         tag : 'span',
33462                         html : this.html
33463                     },
33464                     {
33465                         tag : 'i',
33466                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33467                         tooltip : this.iconTooltip
33468                     }
33469                 ] 
33470             };
33471         }
33472         
33473         return cfg;
33474     },
33475     
33476     initEvents: function() 
33477     {
33478         Roo.bootstrap.Element.superclass.initEvents.call(this);
33479         
33480         this.indicator = this.indicatorEl();
33481         
33482         if(this.indicator){
33483             this.indicator.removeClass('visible');
33484             this.indicator.addClass('invisible');
33485         }
33486         
33487         Roo.bootstrap.form.FieldLabel.register(this);
33488     },
33489     
33490     indicatorEl : function()
33491     {
33492         var indicator = this.el.select('i.roo-required-indicator',true).first();
33493         
33494         if(!indicator){
33495             return false;
33496         }
33497         
33498         return indicator;
33499         
33500     },
33501     
33502     /**
33503      * Mark this field as valid
33504      */
33505     markValid : function()
33506     {
33507         if(this.indicator){
33508             this.indicator.removeClass('visible');
33509             this.indicator.addClass('invisible');
33510         }
33511         if (Roo.bootstrap.version == 3) {
33512             this.el.removeClass(this.invalidClass);
33513             this.el.addClass(this.validClass);
33514         } else {
33515             this.el.removeClass('is-invalid');
33516             this.el.addClass('is-valid');
33517         }
33518         
33519         
33520         this.fireEvent('valid', this);
33521     },
33522     
33523     /**
33524      * Mark this field as invalid
33525      * @param {String} msg The validation message
33526      */
33527     markInvalid : function(msg)
33528     {
33529         if(this.indicator){
33530             this.indicator.removeClass('invisible');
33531             this.indicator.addClass('visible');
33532         }
33533           if (Roo.bootstrap.version == 3) {
33534             this.el.removeClass(this.validClass);
33535             this.el.addClass(this.invalidClass);
33536         } else {
33537             this.el.removeClass('is-valid');
33538             this.el.addClass('is-invalid');
33539         }
33540         
33541         
33542         this.fireEvent('invalid', this, msg);
33543     }
33544     
33545    
33546 });
33547
33548 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33549     
33550     groups: {},
33551     
33552      /**
33553     * register a FieldLabel Group
33554     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33555     */
33556     register : function(label)
33557     {
33558         if(this.groups.hasOwnProperty(label.target)){
33559             return;
33560         }
33561      
33562         this.groups[label.target] = label;
33563         
33564     },
33565     /**
33566     * fetch a FieldLabel Group based on the target
33567     * @param {string} target
33568     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33569     */
33570     get: function(target) {
33571         if (typeof(this.groups[target]) == 'undefined') {
33572             return false;
33573         }
33574         
33575         return this.groups[target] ;
33576     }
33577 });
33578
33579  
33580
33581  /*
33582  * - LGPL
33583  *
33584  * page DateSplitField.
33585  * 
33586  */
33587
33588
33589 /**
33590  * @class Roo.bootstrap.form.DateSplitField
33591  * @extends Roo.bootstrap.Component
33592  * Bootstrap DateSplitField class
33593  * @cfg {string} fieldLabel - the label associated
33594  * @cfg {Number} labelWidth set the width of label (0-12)
33595  * @cfg {String} labelAlign (top|left)
33596  * @cfg {Boolean} dayAllowBlank (true|false) default false
33597  * @cfg {Boolean} monthAllowBlank (true|false) default false
33598  * @cfg {Boolean} yearAllowBlank (true|false) default false
33599  * @cfg {string} dayPlaceholder 
33600  * @cfg {string} monthPlaceholder
33601  * @cfg {string} yearPlaceholder
33602  * @cfg {string} dayFormat default 'd'
33603  * @cfg {string} monthFormat default 'm'
33604  * @cfg {string} yearFormat default 'Y'
33605  * @cfg {Number} labellg set the width of label (1-12)
33606  * @cfg {Number} labelmd set the width of label (1-12)
33607  * @cfg {Number} labelsm set the width of label (1-12)
33608  * @cfg {Number} labelxs set the width of label (1-12)
33609
33610  *     
33611  * @constructor
33612  * Create a new DateSplitField
33613  * @param {Object} config The config object
33614  */
33615
33616 Roo.bootstrap.form.DateSplitField = function(config){
33617     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33618     
33619     this.addEvents({
33620         // raw events
33621          /**
33622          * @event years
33623          * getting the data of years
33624          * @param {Roo.bootstrap.form.DateSplitField} this
33625          * @param {Object} years
33626          */
33627         "years" : true,
33628         /**
33629          * @event days
33630          * getting the data of days
33631          * @param {Roo.bootstrap.form.DateSplitField} this
33632          * @param {Object} days
33633          */
33634         "days" : true,
33635         /**
33636          * @event invalid
33637          * Fires after the field has been marked as invalid.
33638          * @param {Roo.form.Field} this
33639          * @param {String} msg The validation message
33640          */
33641         invalid : true,
33642        /**
33643          * @event valid
33644          * Fires after the field has been validated with no errors.
33645          * @param {Roo.form.Field} this
33646          */
33647         valid : true
33648     });
33649 };
33650
33651 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
33652     
33653     fieldLabel : '',
33654     labelAlign : 'top',
33655     labelWidth : 3,
33656     dayAllowBlank : false,
33657     monthAllowBlank : false,
33658     yearAllowBlank : false,
33659     dayPlaceholder : '',
33660     monthPlaceholder : '',
33661     yearPlaceholder : '',
33662     dayFormat : 'd',
33663     monthFormat : 'm',
33664     yearFormat : 'Y',
33665     isFormField : true,
33666     labellg : 0,
33667     labelmd : 0,
33668     labelsm : 0,
33669     labelxs : 0,
33670     
33671     getAutoCreate : function()
33672     {
33673         var cfg = {
33674             tag : 'div',
33675             cls : 'row roo-date-split-field-group',
33676             cn : [
33677                 {
33678                     tag : 'input',
33679                     type : 'hidden',
33680                     cls : 'form-hidden-field roo-date-split-field-group-value',
33681                     name : this.name
33682                 }
33683             ]
33684         };
33685         
33686         var labelCls = 'col-md-12';
33687         var contentCls = 'col-md-4';
33688         
33689         if(this.fieldLabel){
33690             
33691             var label = {
33692                 tag : 'div',
33693                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33694                 cn : [
33695                     {
33696                         tag : 'label',
33697                         html : this.fieldLabel
33698                     }
33699                 ]
33700             };
33701             
33702             if(this.labelAlign == 'left'){
33703             
33704                 if(this.labelWidth > 12){
33705                     label.style = "width: " + this.labelWidth + 'px';
33706                 }
33707
33708                 if(this.labelWidth < 13 && this.labelmd == 0){
33709                     this.labelmd = this.labelWidth;
33710                 }
33711
33712                 if(this.labellg > 0){
33713                     labelCls = ' col-lg-' + this.labellg;
33714                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33715                 }
33716
33717                 if(this.labelmd > 0){
33718                     labelCls = ' col-md-' + this.labelmd;
33719                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33720                 }
33721
33722                 if(this.labelsm > 0){
33723                     labelCls = ' col-sm-' + this.labelsm;
33724                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33725                 }
33726
33727                 if(this.labelxs > 0){
33728                     labelCls = ' col-xs-' + this.labelxs;
33729                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33730                 }
33731             }
33732             
33733             label.cls += ' ' + labelCls;
33734             
33735             cfg.cn.push(label);
33736         }
33737         
33738         Roo.each(['day', 'month', 'year'], function(t){
33739             cfg.cn.push({
33740                 tag : 'div',
33741                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33742             });
33743         }, this);
33744         
33745         return cfg;
33746     },
33747     
33748     inputEl: function ()
33749     {
33750         return this.el.select('.roo-date-split-field-group-value', true).first();
33751     },
33752     
33753     onRender : function(ct, position) 
33754     {
33755         var _this = this;
33756         
33757         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33758         
33759         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33760         
33761         this.dayField = new Roo.bootstrap.form.ComboBox({
33762             allowBlank : this.dayAllowBlank,
33763             alwaysQuery : true,
33764             displayField : 'value',
33765             editable : false,
33766             fieldLabel : '',
33767             forceSelection : true,
33768             mode : 'local',
33769             placeholder : this.dayPlaceholder,
33770             selectOnFocus : true,
33771             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33772             triggerAction : 'all',
33773             typeAhead : true,
33774             valueField : 'value',
33775             store : new Roo.data.SimpleStore({
33776                 data : (function() {    
33777                     var days = [];
33778                     _this.fireEvent('days', _this, days);
33779                     return days;
33780                 })(),
33781                 fields : [ 'value' ]
33782             }),
33783             listeners : {
33784                 select : function (_self, record, index)
33785                 {
33786                     _this.setValue(_this.getValue());
33787                 }
33788             }
33789         });
33790
33791         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33792         
33793         this.monthField = new Roo.bootstrap.form.MonthField({
33794             after : '<i class=\"fa fa-calendar\"></i>',
33795             allowBlank : this.monthAllowBlank,
33796             placeholder : this.monthPlaceholder,
33797             readOnly : true,
33798             listeners : {
33799                 render : function (_self)
33800                 {
33801                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33802                         e.preventDefault();
33803                         _self.focus();
33804                     });
33805                 },
33806                 select : function (_self, oldvalue, newvalue)
33807                 {
33808                     _this.setValue(_this.getValue());
33809                 }
33810             }
33811         });
33812         
33813         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33814         
33815         this.yearField = new Roo.bootstrap.form.ComboBox({
33816             allowBlank : this.yearAllowBlank,
33817             alwaysQuery : true,
33818             displayField : 'value',
33819             editable : false,
33820             fieldLabel : '',
33821             forceSelection : true,
33822             mode : 'local',
33823             placeholder : this.yearPlaceholder,
33824             selectOnFocus : true,
33825             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33826             triggerAction : 'all',
33827             typeAhead : true,
33828             valueField : 'value',
33829             store : new Roo.data.SimpleStore({
33830                 data : (function() {
33831                     var years = [];
33832                     _this.fireEvent('years', _this, years);
33833                     return years;
33834                 })(),
33835                 fields : [ 'value' ]
33836             }),
33837             listeners : {
33838                 select : function (_self, record, index)
33839                 {
33840                     _this.setValue(_this.getValue());
33841                 }
33842             }
33843         });
33844
33845         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33846     },
33847     
33848     setValue : function(v, format)
33849     {
33850         this.inputEl.dom.value = v;
33851         
33852         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33853         
33854         var d = Date.parseDate(v, f);
33855         
33856         if(!d){
33857             this.validate();
33858             return;
33859         }
33860         
33861         this.setDay(d.format(this.dayFormat));
33862         this.setMonth(d.format(this.monthFormat));
33863         this.setYear(d.format(this.yearFormat));
33864         
33865         this.validate();
33866         
33867         return;
33868     },
33869     
33870     setDay : function(v)
33871     {
33872         this.dayField.setValue(v);
33873         this.inputEl.dom.value = this.getValue();
33874         this.validate();
33875         return;
33876     },
33877     
33878     setMonth : function(v)
33879     {
33880         this.monthField.setValue(v, true);
33881         this.inputEl.dom.value = this.getValue();
33882         this.validate();
33883         return;
33884     },
33885     
33886     setYear : function(v)
33887     {
33888         this.yearField.setValue(v);
33889         this.inputEl.dom.value = this.getValue();
33890         this.validate();
33891         return;
33892     },
33893     
33894     getDay : function()
33895     {
33896         return this.dayField.getValue();
33897     },
33898     
33899     getMonth : function()
33900     {
33901         return this.monthField.getValue();
33902     },
33903     
33904     getYear : function()
33905     {
33906         return this.yearField.getValue();
33907     },
33908     
33909     getValue : function()
33910     {
33911         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33912         
33913         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33914         
33915         return date;
33916     },
33917     
33918     reset : function()
33919     {
33920         this.setDay('');
33921         this.setMonth('');
33922         this.setYear('');
33923         this.inputEl.dom.value = '';
33924         this.validate();
33925         return;
33926     },
33927     
33928     validate : function()
33929     {
33930         var d = this.dayField.validate();
33931         var m = this.monthField.validate();
33932         var y = this.yearField.validate();
33933         
33934         var valid = true;
33935         
33936         if(
33937                 (!this.dayAllowBlank && !d) ||
33938                 (!this.monthAllowBlank && !m) ||
33939                 (!this.yearAllowBlank && !y)
33940         ){
33941             valid = false;
33942         }
33943         
33944         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33945             return valid;
33946         }
33947         
33948         if(valid){
33949             this.markValid();
33950             return valid;
33951         }
33952         
33953         this.markInvalid();
33954         
33955         return valid;
33956     },
33957     
33958     markValid : function()
33959     {
33960         
33961         var label = this.el.select('label', true).first();
33962         var icon = this.el.select('i.fa-star', true).first();
33963
33964         if(label && icon){
33965             icon.remove();
33966         }
33967         
33968         this.fireEvent('valid', this);
33969     },
33970     
33971      /**
33972      * Mark this field as invalid
33973      * @param {String} msg The validation message
33974      */
33975     markInvalid : function(msg)
33976     {
33977         
33978         var label = this.el.select('label', true).first();
33979         var icon = this.el.select('i.fa-star', true).first();
33980
33981         if(label && !icon){
33982             this.el.select('.roo-date-split-field-label', true).createChild({
33983                 tag : 'i',
33984                 cls : 'text-danger fa fa-lg fa-star',
33985                 tooltip : 'This field is required',
33986                 style : 'margin-right:5px;'
33987             }, label, true);
33988         }
33989         
33990         this.fireEvent('invalid', this, msg);
33991     },
33992     
33993     clearInvalid : function()
33994     {
33995         var label = this.el.select('label', true).first();
33996         var icon = this.el.select('i.fa-star', true).first();
33997
33998         if(label && icon){
33999             icon.remove();
34000         }
34001         
34002         this.fireEvent('valid', this);
34003     },
34004     
34005     getName: function()
34006     {
34007         return this.name;
34008     }
34009     
34010 });
34011
34012  
34013
34014 /**
34015  * @class Roo.bootstrap.LayoutMasonry
34016  * @extends Roo.bootstrap.Component
34017  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34018  * Bootstrap Layout Masonry class
34019  *
34020  * This is based on 
34021  * http://masonry.desandro.com
34022  *
34023  * The idea is to render all the bricks based on vertical width...
34024  *
34025  * The original code extends 'outlayer' - we might need to use that....
34026
34027  * @constructor
34028  * Create a new Element
34029  * @param {Object} config The config object
34030  */
34031
34032 Roo.bootstrap.LayoutMasonry = function(config){
34033     
34034     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34035     
34036     this.bricks = [];
34037     
34038     Roo.bootstrap.LayoutMasonry.register(this);
34039     
34040     this.addEvents({
34041         // raw events
34042         /**
34043          * @event layout
34044          * Fire after layout the items
34045          * @param {Roo.bootstrap.LayoutMasonry} this
34046          * @param {Roo.EventObject} e
34047          */
34048         "layout" : true
34049     });
34050     
34051 };
34052
34053 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34054     
34055     /**
34056      * @cfg {Boolean} isLayoutInstant = no animation?
34057      */   
34058     isLayoutInstant : false, // needed?
34059    
34060     /**
34061      * @cfg {Number} boxWidth  width of the columns
34062      */   
34063     boxWidth : 450,
34064     
34065       /**
34066      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34067      */   
34068     boxHeight : 0,
34069     
34070     /**
34071      * @cfg {Number} padWidth padding below box..
34072      */   
34073     padWidth : 10, 
34074     
34075     /**
34076      * @cfg {Number} gutter gutter width..
34077      */   
34078     gutter : 10,
34079     
34080      /**
34081      * @cfg {Number} maxCols maximum number of columns
34082      */   
34083     
34084     maxCols: 0,
34085     
34086     /**
34087      * @cfg {Boolean} isAutoInitial defalut true
34088      */   
34089     isAutoInitial : true, 
34090     
34091     containerWidth: 0,
34092     
34093     /**
34094      * @cfg {Boolean} isHorizontal defalut false
34095      */   
34096     isHorizontal : false, 
34097
34098     currentSize : null,
34099     
34100     tag: 'div',
34101     
34102     cls: '',
34103     
34104     bricks: null, //CompositeElement
34105     
34106     cols : 1,
34107     
34108     _isLayoutInited : false,
34109     
34110 //    isAlternative : false, // only use for vertical layout...
34111     
34112     /**
34113      * @cfg {Number} alternativePadWidth padding below box..
34114      */   
34115     alternativePadWidth : 50,
34116     
34117     selectedBrick : [],
34118     
34119     getAutoCreate : function(){
34120         
34121         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34122         
34123         var cfg = {
34124             tag: this.tag,
34125             cls: 'blog-masonary-wrapper ' + this.cls,
34126             cn : {
34127                 cls : 'mas-boxes masonary'
34128             }
34129         };
34130         
34131         return cfg;
34132     },
34133     
34134     getChildContainer: function( )
34135     {
34136         if (this.boxesEl) {
34137             return this.boxesEl;
34138         }
34139         
34140         this.boxesEl = this.el.select('.mas-boxes').first();
34141         
34142         return this.boxesEl;
34143     },
34144     
34145     
34146     initEvents : function()
34147     {
34148         var _this = this;
34149         
34150         if(this.isAutoInitial){
34151             Roo.log('hook children rendered');
34152             this.on('childrenrendered', function() {
34153                 Roo.log('children rendered');
34154                 _this.initial();
34155             } ,this);
34156         }
34157     },
34158     
34159     initial : function()
34160     {
34161         this.selectedBrick = [];
34162         
34163         this.currentSize = this.el.getBox(true);
34164         
34165         Roo.EventManager.onWindowResize(this.resize, this); 
34166
34167         if(!this.isAutoInitial){
34168             this.layout();
34169             return;
34170         }
34171         
34172         this.layout();
34173         
34174         return;
34175         //this.layout.defer(500,this);
34176         
34177     },
34178     
34179     resize : function()
34180     {
34181         var cs = this.el.getBox(true);
34182         
34183         if (
34184                 this.currentSize.width == cs.width && 
34185                 this.currentSize.x == cs.x && 
34186                 this.currentSize.height == cs.height && 
34187                 this.currentSize.y == cs.y 
34188         ) {
34189             Roo.log("no change in with or X or Y");
34190             return;
34191         }
34192         
34193         this.currentSize = cs;
34194         
34195         this.layout();
34196         
34197     },
34198     
34199     layout : function()
34200     {   
34201         this._resetLayout();
34202         
34203         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34204         
34205         this.layoutItems( isInstant );
34206       
34207         this._isLayoutInited = true;
34208         
34209         this.fireEvent('layout', this);
34210         
34211     },
34212     
34213     _resetLayout : function()
34214     {
34215         if(this.isHorizontal){
34216             this.horizontalMeasureColumns();
34217             return;
34218         }
34219         
34220         this.verticalMeasureColumns();
34221         
34222     },
34223     
34224     verticalMeasureColumns : function()
34225     {
34226         this.getContainerWidth();
34227         
34228 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34229 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34230 //            return;
34231 //        }
34232         
34233         var boxWidth = this.boxWidth + this.padWidth;
34234         
34235         if(this.containerWidth < this.boxWidth){
34236             boxWidth = this.containerWidth
34237         }
34238         
34239         var containerWidth = this.containerWidth;
34240         
34241         var cols = Math.floor(containerWidth / boxWidth);
34242         
34243         this.cols = Math.max( cols, 1 );
34244         
34245         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34246         
34247         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34248         
34249         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34250         
34251         this.colWidth = boxWidth + avail - this.padWidth;
34252         
34253         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34254         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34255     },
34256     
34257     horizontalMeasureColumns : function()
34258     {
34259         this.getContainerWidth();
34260         
34261         var boxWidth = this.boxWidth;
34262         
34263         if(this.containerWidth < boxWidth){
34264             boxWidth = this.containerWidth;
34265         }
34266         
34267         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34268         
34269         this.el.setHeight(boxWidth);
34270         
34271     },
34272     
34273     getContainerWidth : function()
34274     {
34275         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34276     },
34277     
34278     layoutItems : function( isInstant )
34279     {
34280         Roo.log(this.bricks);
34281         
34282         var items = Roo.apply([], this.bricks);
34283         
34284         if(this.isHorizontal){
34285             this._horizontalLayoutItems( items , isInstant );
34286             return;
34287         }
34288         
34289 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34290 //            this._verticalAlternativeLayoutItems( items , isInstant );
34291 //            return;
34292 //        }
34293         
34294         this._verticalLayoutItems( items , isInstant );
34295         
34296     },
34297     
34298     _verticalLayoutItems : function ( items , isInstant)
34299     {
34300         if ( !items || !items.length ) {
34301             return;
34302         }
34303         
34304         var standard = [
34305             ['xs', 'xs', 'xs', 'tall'],
34306             ['xs', 'xs', 'tall'],
34307             ['xs', 'xs', 'sm'],
34308             ['xs', 'xs', 'xs'],
34309             ['xs', 'tall'],
34310             ['xs', 'sm'],
34311             ['xs', 'xs'],
34312             ['xs'],
34313             
34314             ['sm', 'xs', 'xs'],
34315             ['sm', 'xs'],
34316             ['sm'],
34317             
34318             ['tall', 'xs', 'xs', 'xs'],
34319             ['tall', 'xs', 'xs'],
34320             ['tall', 'xs'],
34321             ['tall']
34322             
34323         ];
34324         
34325         var queue = [];
34326         
34327         var boxes = [];
34328         
34329         var box = [];
34330         
34331         Roo.each(items, function(item, k){
34332             
34333             switch (item.size) {
34334                 // these layouts take up a full box,
34335                 case 'md' :
34336                 case 'md-left' :
34337                 case 'md-right' :
34338                 case 'wide' :
34339                     
34340                     if(box.length){
34341                         boxes.push(box);
34342                         box = [];
34343                     }
34344                     
34345                     boxes.push([item]);
34346                     
34347                     break;
34348                     
34349                 case 'xs' :
34350                 case 'sm' :
34351                 case 'tall' :
34352                     
34353                     box.push(item);
34354                     
34355                     break;
34356                 default :
34357                     break;
34358                     
34359             }
34360             
34361         }, this);
34362         
34363         if(box.length){
34364             boxes.push(box);
34365             box = [];
34366         }
34367         
34368         var filterPattern = function(box, length)
34369         {
34370             if(!box.length){
34371                 return;
34372             }
34373             
34374             var match = false;
34375             
34376             var pattern = box.slice(0, length);
34377             
34378             var format = [];
34379             
34380             Roo.each(pattern, function(i){
34381                 format.push(i.size);
34382             }, this);
34383             
34384             Roo.each(standard, function(s){
34385                 
34386                 if(String(s) != String(format)){
34387                     return;
34388                 }
34389                 
34390                 match = true;
34391                 return false;
34392                 
34393             }, this);
34394             
34395             if(!match && length == 1){
34396                 return;
34397             }
34398             
34399             if(!match){
34400                 filterPattern(box, length - 1);
34401                 return;
34402             }
34403                 
34404             queue.push(pattern);
34405
34406             box = box.slice(length, box.length);
34407
34408             filterPattern(box, 4);
34409
34410             return;
34411             
34412         }
34413         
34414         Roo.each(boxes, function(box, k){
34415             
34416             if(!box.length){
34417                 return;
34418             }
34419             
34420             if(box.length == 1){
34421                 queue.push(box);
34422                 return;
34423             }
34424             
34425             filterPattern(box, 4);
34426             
34427         }, this);
34428         
34429         this._processVerticalLayoutQueue( queue, isInstant );
34430         
34431     },
34432     
34433 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34434 //    {
34435 //        if ( !items || !items.length ) {
34436 //            return;
34437 //        }
34438 //
34439 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34440 //        
34441 //    },
34442     
34443     _horizontalLayoutItems : function ( items , isInstant)
34444     {
34445         if ( !items || !items.length || items.length < 3) {
34446             return;
34447         }
34448         
34449         items.reverse();
34450         
34451         var eItems = items.slice(0, 3);
34452         
34453         items = items.slice(3, items.length);
34454         
34455         var standard = [
34456             ['xs', 'xs', 'xs', 'wide'],
34457             ['xs', 'xs', 'wide'],
34458             ['xs', 'xs', 'sm'],
34459             ['xs', 'xs', 'xs'],
34460             ['xs', 'wide'],
34461             ['xs', 'sm'],
34462             ['xs', 'xs'],
34463             ['xs'],
34464             
34465             ['sm', 'xs', 'xs'],
34466             ['sm', 'xs'],
34467             ['sm'],
34468             
34469             ['wide', 'xs', 'xs', 'xs'],
34470             ['wide', 'xs', 'xs'],
34471             ['wide', 'xs'],
34472             ['wide'],
34473             
34474             ['wide-thin']
34475         ];
34476         
34477         var queue = [];
34478         
34479         var boxes = [];
34480         
34481         var box = [];
34482         
34483         Roo.each(items, function(item, k){
34484             
34485             switch (item.size) {
34486                 case 'md' :
34487                 case 'md-left' :
34488                 case 'md-right' :
34489                 case 'tall' :
34490                     
34491                     if(box.length){
34492                         boxes.push(box);
34493                         box = [];
34494                     }
34495                     
34496                     boxes.push([item]);
34497                     
34498                     break;
34499                     
34500                 case 'xs' :
34501                 case 'sm' :
34502                 case 'wide' :
34503                 case 'wide-thin' :
34504                     
34505                     box.push(item);
34506                     
34507                     break;
34508                 default :
34509                     break;
34510                     
34511             }
34512             
34513         }, this);
34514         
34515         if(box.length){
34516             boxes.push(box);
34517             box = [];
34518         }
34519         
34520         var filterPattern = function(box, length)
34521         {
34522             if(!box.length){
34523                 return;
34524             }
34525             
34526             var match = false;
34527             
34528             var pattern = box.slice(0, length);
34529             
34530             var format = [];
34531             
34532             Roo.each(pattern, function(i){
34533                 format.push(i.size);
34534             }, this);
34535             
34536             Roo.each(standard, function(s){
34537                 
34538                 if(String(s) != String(format)){
34539                     return;
34540                 }
34541                 
34542                 match = true;
34543                 return false;
34544                 
34545             }, this);
34546             
34547             if(!match && length == 1){
34548                 return;
34549             }
34550             
34551             if(!match){
34552                 filterPattern(box, length - 1);
34553                 return;
34554             }
34555                 
34556             queue.push(pattern);
34557
34558             box = box.slice(length, box.length);
34559
34560             filterPattern(box, 4);
34561
34562             return;
34563             
34564         }
34565         
34566         Roo.each(boxes, function(box, k){
34567             
34568             if(!box.length){
34569                 return;
34570             }
34571             
34572             if(box.length == 1){
34573                 queue.push(box);
34574                 return;
34575             }
34576             
34577             filterPattern(box, 4);
34578             
34579         }, this);
34580         
34581         
34582         var prune = [];
34583         
34584         var pos = this.el.getBox(true);
34585         
34586         var minX = pos.x;
34587         
34588         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34589         
34590         var hit_end = false;
34591         
34592         Roo.each(queue, function(box){
34593             
34594             if(hit_end){
34595                 
34596                 Roo.each(box, function(b){
34597                 
34598                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34599                     b.el.hide();
34600
34601                 }, this);
34602
34603                 return;
34604             }
34605             
34606             var mx = 0;
34607             
34608             Roo.each(box, function(b){
34609                 
34610                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34611                 b.el.show();
34612
34613                 mx = Math.max(mx, b.x);
34614                 
34615             }, this);
34616             
34617             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34618             
34619             if(maxX < minX){
34620                 
34621                 Roo.each(box, function(b){
34622                 
34623                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34624                     b.el.hide();
34625                     
34626                 }, this);
34627                 
34628                 hit_end = true;
34629                 
34630                 return;
34631             }
34632             
34633             prune.push(box);
34634             
34635         }, this);
34636         
34637         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34638     },
34639     
34640     /** Sets position of item in DOM
34641     * @param {Element} item
34642     * @param {Number} x - horizontal position
34643     * @param {Number} y - vertical position
34644     * @param {Boolean} isInstant - disables transitions
34645     */
34646     _processVerticalLayoutQueue : function( queue, isInstant )
34647     {
34648         var pos = this.el.getBox(true);
34649         var x = pos.x;
34650         var y = pos.y;
34651         var maxY = [];
34652         
34653         for (var i = 0; i < this.cols; i++){
34654             maxY[i] = pos.y;
34655         }
34656         
34657         Roo.each(queue, function(box, k){
34658             
34659             var col = k % this.cols;
34660             
34661             Roo.each(box, function(b,kk){
34662                 
34663                 b.el.position('absolute');
34664                 
34665                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34666                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34667                 
34668                 if(b.size == 'md-left' || b.size == 'md-right'){
34669                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34670                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34671                 }
34672                 
34673                 b.el.setWidth(width);
34674                 b.el.setHeight(height);
34675                 // iframe?
34676                 b.el.select('iframe',true).setSize(width,height);
34677                 
34678             }, this);
34679             
34680             for (var i = 0; i < this.cols; i++){
34681                 
34682                 if(maxY[i] < maxY[col]){
34683                     col = i;
34684                     continue;
34685                 }
34686                 
34687                 col = Math.min(col, i);
34688                 
34689             }
34690             
34691             x = pos.x + col * (this.colWidth + this.padWidth);
34692             
34693             y = maxY[col];
34694             
34695             var positions = [];
34696             
34697             switch (box.length){
34698                 case 1 :
34699                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34700                     break;
34701                 case 2 :
34702                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34703                     break;
34704                 case 3 :
34705                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34706                     break;
34707                 case 4 :
34708                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34709                     break;
34710                 default :
34711                     break;
34712             }
34713             
34714             Roo.each(box, function(b,kk){
34715                 
34716                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34717                 
34718                 var sz = b.el.getSize();
34719                 
34720                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34721                 
34722             }, this);
34723             
34724         }, this);
34725         
34726         var mY = 0;
34727         
34728         for (var i = 0; i < this.cols; i++){
34729             mY = Math.max(mY, maxY[i]);
34730         }
34731         
34732         this.el.setHeight(mY - pos.y);
34733         
34734     },
34735     
34736 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34737 //    {
34738 //        var pos = this.el.getBox(true);
34739 //        var x = pos.x;
34740 //        var y = pos.y;
34741 //        var maxX = pos.right;
34742 //        
34743 //        var maxHeight = 0;
34744 //        
34745 //        Roo.each(items, function(item, k){
34746 //            
34747 //            var c = k % 2;
34748 //            
34749 //            item.el.position('absolute');
34750 //                
34751 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34752 //
34753 //            item.el.setWidth(width);
34754 //
34755 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34756 //
34757 //            item.el.setHeight(height);
34758 //            
34759 //            if(c == 0){
34760 //                item.el.setXY([x, y], isInstant ? false : true);
34761 //            } else {
34762 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34763 //            }
34764 //            
34765 //            y = y + height + this.alternativePadWidth;
34766 //            
34767 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34768 //            
34769 //        }, this);
34770 //        
34771 //        this.el.setHeight(maxHeight);
34772 //        
34773 //    },
34774     
34775     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34776     {
34777         var pos = this.el.getBox(true);
34778         
34779         var minX = pos.x;
34780         var minY = pos.y;
34781         
34782         var maxX = pos.right;
34783         
34784         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34785         
34786         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34787         
34788         Roo.each(queue, function(box, k){
34789             
34790             Roo.each(box, function(b, kk){
34791                 
34792                 b.el.position('absolute');
34793                 
34794                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34795                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34796                 
34797                 if(b.size == 'md-left' || b.size == 'md-right'){
34798                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34799                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34800                 }
34801                 
34802                 b.el.setWidth(width);
34803                 b.el.setHeight(height);
34804                 
34805             }, this);
34806             
34807             if(!box.length){
34808                 return;
34809             }
34810             
34811             var positions = [];
34812             
34813             switch (box.length){
34814                 case 1 :
34815                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34816                     break;
34817                 case 2 :
34818                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34819                     break;
34820                 case 3 :
34821                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34822                     break;
34823                 case 4 :
34824                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34825                     break;
34826                 default :
34827                     break;
34828             }
34829             
34830             Roo.each(box, function(b,kk){
34831                 
34832                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34833                 
34834                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34835                 
34836             }, this);
34837             
34838         }, this);
34839         
34840     },
34841     
34842     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34843     {
34844         Roo.each(eItems, function(b,k){
34845             
34846             b.size = (k == 0) ? 'sm' : 'xs';
34847             b.x = (k == 0) ? 2 : 1;
34848             b.y = (k == 0) ? 2 : 1;
34849             
34850             b.el.position('absolute');
34851             
34852             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34853                 
34854             b.el.setWidth(width);
34855             
34856             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34857             
34858             b.el.setHeight(height);
34859             
34860         }, this);
34861
34862         var positions = [];
34863         
34864         positions.push({
34865             x : maxX - this.unitWidth * 2 - this.gutter,
34866             y : minY
34867         });
34868         
34869         positions.push({
34870             x : maxX - this.unitWidth,
34871             y : minY + (this.unitWidth + this.gutter) * 2
34872         });
34873         
34874         positions.push({
34875             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34876             y : minY
34877         });
34878         
34879         Roo.each(eItems, function(b,k){
34880             
34881             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34882
34883         }, this);
34884         
34885     },
34886     
34887     getVerticalOneBoxColPositions : function(x, y, box)
34888     {
34889         var pos = [];
34890         
34891         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34892         
34893         if(box[0].size == 'md-left'){
34894             rand = 0;
34895         }
34896         
34897         if(box[0].size == 'md-right'){
34898             rand = 1;
34899         }
34900         
34901         pos.push({
34902             x : x + (this.unitWidth + this.gutter) * rand,
34903             y : y
34904         });
34905         
34906         return pos;
34907     },
34908     
34909     getVerticalTwoBoxColPositions : function(x, y, box)
34910     {
34911         var pos = [];
34912         
34913         if(box[0].size == 'xs'){
34914             
34915             pos.push({
34916                 x : x,
34917                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34918             });
34919
34920             pos.push({
34921                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34922                 y : y
34923             });
34924             
34925             return pos;
34926             
34927         }
34928         
34929         pos.push({
34930             x : x,
34931             y : y
34932         });
34933
34934         pos.push({
34935             x : x + (this.unitWidth + this.gutter) * 2,
34936             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34937         });
34938         
34939         return pos;
34940         
34941     },
34942     
34943     getVerticalThreeBoxColPositions : function(x, y, box)
34944     {
34945         var pos = [];
34946         
34947         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34948             
34949             pos.push({
34950                 x : x,
34951                 y : y
34952             });
34953
34954             pos.push({
34955                 x : x + (this.unitWidth + this.gutter) * 1,
34956                 y : y
34957             });
34958             
34959             pos.push({
34960                 x : x + (this.unitWidth + this.gutter) * 2,
34961                 y : y
34962             });
34963             
34964             return pos;
34965             
34966         }
34967         
34968         if(box[0].size == 'xs' && box[1].size == 'xs'){
34969             
34970             pos.push({
34971                 x : x,
34972                 y : y
34973             });
34974
34975             pos.push({
34976                 x : x,
34977                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34978             });
34979             
34980             pos.push({
34981                 x : x + (this.unitWidth + this.gutter) * 1,
34982                 y : y
34983             });
34984             
34985             return pos;
34986             
34987         }
34988         
34989         pos.push({
34990             x : x,
34991             y : y
34992         });
34993
34994         pos.push({
34995             x : x + (this.unitWidth + this.gutter) * 2,
34996             y : y
34997         });
34998
34999         pos.push({
35000             x : x + (this.unitWidth + this.gutter) * 2,
35001             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35002         });
35003             
35004         return pos;
35005         
35006     },
35007     
35008     getVerticalFourBoxColPositions : function(x, y, box)
35009     {
35010         var pos = [];
35011         
35012         if(box[0].size == 'xs'){
35013             
35014             pos.push({
35015                 x : x,
35016                 y : y
35017             });
35018
35019             pos.push({
35020                 x : x,
35021                 y : y + (this.unitHeight + this.gutter) * 1
35022             });
35023             
35024             pos.push({
35025                 x : x,
35026                 y : y + (this.unitHeight + this.gutter) * 2
35027             });
35028             
35029             pos.push({
35030                 x : x + (this.unitWidth + this.gutter) * 1,
35031                 y : y
35032             });
35033             
35034             return pos;
35035             
35036         }
35037         
35038         pos.push({
35039             x : x,
35040             y : y
35041         });
35042
35043         pos.push({
35044             x : x + (this.unitWidth + this.gutter) * 2,
35045             y : y
35046         });
35047
35048         pos.push({
35049             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35050             y : y + (this.unitHeight + this.gutter) * 1
35051         });
35052
35053         pos.push({
35054             x : x + (this.unitWidth + this.gutter) * 2,
35055             y : y + (this.unitWidth + this.gutter) * 2
35056         });
35057
35058         return pos;
35059         
35060     },
35061     
35062     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35063     {
35064         var pos = [];
35065         
35066         if(box[0].size == 'md-left'){
35067             pos.push({
35068                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35069                 y : minY
35070             });
35071             
35072             return pos;
35073         }
35074         
35075         if(box[0].size == 'md-right'){
35076             pos.push({
35077                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35078                 y : minY + (this.unitWidth + this.gutter) * 1
35079             });
35080             
35081             return pos;
35082         }
35083         
35084         var rand = Math.floor(Math.random() * (4 - box[0].y));
35085         
35086         pos.push({
35087             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35088             y : minY + (this.unitWidth + this.gutter) * rand
35089         });
35090         
35091         return pos;
35092         
35093     },
35094     
35095     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35096     {
35097         var pos = [];
35098         
35099         if(box[0].size == 'xs'){
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) * (3 - box[1].y)
35109             });
35110             
35111             return pos;
35112             
35113         }
35114         
35115         pos.push({
35116             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35117             y : minY
35118         });
35119
35120         pos.push({
35121             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35122             y : minY + (this.unitWidth + this.gutter) * 2
35123         });
35124         
35125         return pos;
35126         
35127     },
35128     
35129     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35130     {
35131         var pos = [];
35132         
35133         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35134             
35135             pos.push({
35136                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35137                 y : minY
35138             });
35139
35140             pos.push({
35141                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35142                 y : minY + (this.unitWidth + this.gutter) * 1
35143             });
35144             
35145             pos.push({
35146                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35147                 y : minY + (this.unitWidth + this.gutter) * 2
35148             });
35149             
35150             return pos;
35151             
35152         }
35153         
35154         if(box[0].size == 'xs' && box[1].size == 'xs'){
35155             
35156             pos.push({
35157                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35158                 y : minY
35159             });
35160
35161             pos.push({
35162                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35163                 y : minY
35164             });
35165             
35166             pos.push({
35167                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35168                 y : minY + (this.unitWidth + this.gutter) * 1
35169             });
35170             
35171             return pos;
35172             
35173         }
35174         
35175         pos.push({
35176             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35177             y : minY
35178         });
35179
35180         pos.push({
35181             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35182             y : minY + (this.unitWidth + this.gutter) * 2
35183         });
35184
35185         pos.push({
35186             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35187             y : minY + (this.unitWidth + this.gutter) * 2
35188         });
35189             
35190         return pos;
35191         
35192     },
35193     
35194     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35195     {
35196         var pos = [];
35197         
35198         if(box[0].size == 'xs'){
35199             
35200             pos.push({
35201                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35202                 y : minY
35203             });
35204
35205             pos.push({
35206                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35207                 y : minY
35208             });
35209             
35210             pos.push({
35211                 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),
35212                 y : minY
35213             });
35214             
35215             pos.push({
35216                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35217                 y : minY + (this.unitWidth + this.gutter) * 1
35218             });
35219             
35220             return pos;
35221             
35222         }
35223         
35224         pos.push({
35225             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35226             y : minY
35227         });
35228         
35229         pos.push({
35230             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35231             y : minY + (this.unitWidth + this.gutter) * 2
35232         });
35233         
35234         pos.push({
35235             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35236             y : minY + (this.unitWidth + this.gutter) * 2
35237         });
35238         
35239         pos.push({
35240             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),
35241             y : minY + (this.unitWidth + this.gutter) * 2
35242         });
35243
35244         return pos;
35245         
35246     },
35247     
35248     /**
35249     * remove a Masonry Brick
35250     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35251     */
35252     removeBrick : function(brick_id)
35253     {
35254         if (!brick_id) {
35255             return;
35256         }
35257         
35258         for (var i = 0; i<this.bricks.length; i++) {
35259             if (this.bricks[i].id == brick_id) {
35260                 this.bricks.splice(i,1);
35261                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35262                 this.initial();
35263             }
35264         }
35265     },
35266     
35267     /**
35268     * adds a Masonry Brick
35269     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35270     */
35271     addBrick : function(cfg)
35272     {
35273         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35274         //this.register(cn);
35275         cn.parentId = this.id;
35276         cn.render(this.el);
35277         return cn;
35278     },
35279     
35280     /**
35281     * register a Masonry Brick
35282     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35283     */
35284     
35285     register : function(brick)
35286     {
35287         this.bricks.push(brick);
35288         brick.masonryId = this.id;
35289     },
35290     
35291     /**
35292     * clear all the Masonry Brick
35293     */
35294     clearAll : function()
35295     {
35296         this.bricks = [];
35297         //this.getChildContainer().dom.innerHTML = "";
35298         this.el.dom.innerHTML = '';
35299     },
35300     
35301     getSelected : function()
35302     {
35303         if (!this.selectedBrick) {
35304             return false;
35305         }
35306         
35307         return this.selectedBrick;
35308     }
35309 });
35310
35311 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35312     
35313     groups: {},
35314      /**
35315     * register a Masonry Layout
35316     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35317     */
35318     
35319     register : function(layout)
35320     {
35321         this.groups[layout.id] = layout;
35322     },
35323     /**
35324     * fetch a  Masonry Layout based on the masonry layout ID
35325     * @param {string} the masonry layout to add
35326     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35327     */
35328     
35329     get: function(layout_id) {
35330         if (typeof(this.groups[layout_id]) == 'undefined') {
35331             return false;
35332         }
35333         return this.groups[layout_id] ;
35334     }
35335     
35336     
35337     
35338 });
35339
35340  
35341
35342  /**
35343  *
35344  * This is based on 
35345  * http://masonry.desandro.com
35346  *
35347  * The idea is to render all the bricks based on vertical width...
35348  *
35349  * The original code extends 'outlayer' - we might need to use that....
35350  * 
35351  */
35352
35353
35354 /**
35355  * @class Roo.bootstrap.LayoutMasonryAuto
35356  * @extends Roo.bootstrap.Component
35357  * Bootstrap Layout Masonry class
35358  * 
35359  * @constructor
35360  * Create a new Element
35361  * @param {Object} config The config object
35362  */
35363
35364 Roo.bootstrap.LayoutMasonryAuto = function(config){
35365     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35366 };
35367
35368 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35369     
35370       /**
35371      * @cfg {Boolean} isFitWidth  - resize the width..
35372      */   
35373     isFitWidth : false,  // options..
35374     /**
35375      * @cfg {Boolean} isOriginLeft = left align?
35376      */   
35377     isOriginLeft : true,
35378     /**
35379      * @cfg {Boolean} isOriginTop = top align?
35380      */   
35381     isOriginTop : false,
35382     /**
35383      * @cfg {Boolean} isLayoutInstant = no animation?
35384      */   
35385     isLayoutInstant : false, // needed?
35386     /**
35387      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35388      */   
35389     isResizingContainer : true,
35390     /**
35391      * @cfg {Number} columnWidth  width of the columns 
35392      */   
35393     
35394     columnWidth : 0,
35395     
35396     /**
35397      * @cfg {Number} maxCols maximum number of columns
35398      */   
35399     
35400     maxCols: 0,
35401     /**
35402      * @cfg {Number} padHeight padding below box..
35403      */   
35404     
35405     padHeight : 10, 
35406     
35407     /**
35408      * @cfg {Boolean} isAutoInitial defalut true
35409      */   
35410     
35411     isAutoInitial : true, 
35412     
35413     // private?
35414     gutter : 0,
35415     
35416     containerWidth: 0,
35417     initialColumnWidth : 0,
35418     currentSize : null,
35419     
35420     colYs : null, // array.
35421     maxY : 0,
35422     padWidth: 10,
35423     
35424     
35425     tag: 'div',
35426     cls: '',
35427     bricks: null, //CompositeElement
35428     cols : 0, // array?
35429     // element : null, // wrapped now this.el
35430     _isLayoutInited : null, 
35431     
35432     
35433     getAutoCreate : function(){
35434         
35435         var cfg = {
35436             tag: this.tag,
35437             cls: 'blog-masonary-wrapper ' + this.cls,
35438             cn : {
35439                 cls : 'mas-boxes masonary'
35440             }
35441         };
35442         
35443         return cfg;
35444     },
35445     
35446     getChildContainer: function( )
35447     {
35448         if (this.boxesEl) {
35449             return this.boxesEl;
35450         }
35451         
35452         this.boxesEl = this.el.select('.mas-boxes').first();
35453         
35454         return this.boxesEl;
35455     },
35456     
35457     
35458     initEvents : function()
35459     {
35460         var _this = this;
35461         
35462         if(this.isAutoInitial){
35463             Roo.log('hook children rendered');
35464             this.on('childrenrendered', function() {
35465                 Roo.log('children rendered');
35466                 _this.initial();
35467             } ,this);
35468         }
35469         
35470     },
35471     
35472     initial : function()
35473     {
35474         this.reloadItems();
35475
35476         this.currentSize = this.el.getBox(true);
35477
35478         /// was window resize... - let's see if this works..
35479         Roo.EventManager.onWindowResize(this.resize, this); 
35480
35481         if(!this.isAutoInitial){
35482             this.layout();
35483             return;
35484         }
35485         
35486         this.layout.defer(500,this);
35487     },
35488     
35489     reloadItems: function()
35490     {
35491         this.bricks = this.el.select('.masonry-brick', true);
35492         
35493         this.bricks.each(function(b) {
35494             //Roo.log(b.getSize());
35495             if (!b.attr('originalwidth')) {
35496                 b.attr('originalwidth',  b.getSize().width);
35497             }
35498             
35499         });
35500         
35501         Roo.log(this.bricks.elements.length);
35502     },
35503     
35504     resize : function()
35505     {
35506         Roo.log('resize');
35507         var cs = this.el.getBox(true);
35508         
35509         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35510             Roo.log("no change in with or X");
35511             return;
35512         }
35513         this.currentSize = cs;
35514         this.layout();
35515     },
35516     
35517     layout : function()
35518     {
35519          Roo.log('layout');
35520         this._resetLayout();
35521         //this._manageStamps();
35522       
35523         // don't animate first layout
35524         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35525         this.layoutItems( isInstant );
35526       
35527         // flag for initalized
35528         this._isLayoutInited = true;
35529     },
35530     
35531     layoutItems : function( isInstant )
35532     {
35533         //var items = this._getItemsForLayout( this.items );
35534         // original code supports filtering layout items.. we just ignore it..
35535         
35536         this._layoutItems( this.bricks , isInstant );
35537       
35538         this._postLayout();
35539     },
35540     _layoutItems : function ( items , isInstant)
35541     {
35542        //this.fireEvent( 'layout', this, items );
35543     
35544
35545         if ( !items || !items.elements.length ) {
35546           // no items, emit event with empty array
35547             return;
35548         }
35549
35550         var queue = [];
35551         items.each(function(item) {
35552             Roo.log("layout item");
35553             Roo.log(item);
35554             // get x/y object from method
35555             var position = this._getItemLayoutPosition( item );
35556             // enqueue
35557             position.item = item;
35558             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35559             queue.push( position );
35560         }, this);
35561       
35562         this._processLayoutQueue( queue );
35563     },
35564     /** Sets position of item in DOM
35565     * @param {Element} item
35566     * @param {Number} x - horizontal position
35567     * @param {Number} y - vertical position
35568     * @param {Boolean} isInstant - disables transitions
35569     */
35570     _processLayoutQueue : function( queue )
35571     {
35572         for ( var i=0, len = queue.length; i < len; i++ ) {
35573             var obj = queue[i];
35574             obj.item.position('absolute');
35575             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35576         }
35577     },
35578       
35579     
35580     /**
35581     * Any logic you want to do after each layout,
35582     * i.e. size the container
35583     */
35584     _postLayout : function()
35585     {
35586         this.resizeContainer();
35587     },
35588     
35589     resizeContainer : function()
35590     {
35591         if ( !this.isResizingContainer ) {
35592             return;
35593         }
35594         var size = this._getContainerSize();
35595         if ( size ) {
35596             this.el.setSize(size.width,size.height);
35597             this.boxesEl.setSize(size.width,size.height);
35598         }
35599     },
35600     
35601     
35602     
35603     _resetLayout : function()
35604     {
35605         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35606         this.colWidth = this.el.getWidth();
35607         //this.gutter = this.el.getWidth(); 
35608         
35609         this.measureColumns();
35610
35611         // reset column Y
35612         var i = this.cols;
35613         this.colYs = [];
35614         while (i--) {
35615             this.colYs.push( 0 );
35616         }
35617     
35618         this.maxY = 0;
35619     },
35620
35621     measureColumns : function()
35622     {
35623         this.getContainerWidth();
35624       // if columnWidth is 0, default to outerWidth of first item
35625         if ( !this.columnWidth ) {
35626             var firstItem = this.bricks.first();
35627             Roo.log(firstItem);
35628             this.columnWidth  = this.containerWidth;
35629             if (firstItem && firstItem.attr('originalwidth') ) {
35630                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35631             }
35632             // columnWidth fall back to item of first element
35633             Roo.log("set column width?");
35634                         this.initialColumnWidth = this.columnWidth  ;
35635
35636             // if first elem has no width, default to size of container
35637             
35638         }
35639         
35640         
35641         if (this.initialColumnWidth) {
35642             this.columnWidth = this.initialColumnWidth;
35643         }
35644         
35645         
35646             
35647         // column width is fixed at the top - however if container width get's smaller we should
35648         // reduce it...
35649         
35650         // this bit calcs how man columns..
35651             
35652         var columnWidth = this.columnWidth += this.gutter;
35653       
35654         // calculate columns
35655         var containerWidth = this.containerWidth + this.gutter;
35656         
35657         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35658         // fix rounding errors, typically with gutters
35659         var excess = columnWidth - containerWidth % columnWidth;
35660         
35661         
35662         // if overshoot is less than a pixel, round up, otherwise floor it
35663         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35664         cols = Math[ mathMethod ]( cols );
35665         this.cols = Math.max( cols, 1 );
35666         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35667         
35668          // padding positioning..
35669         var totalColWidth = this.cols * this.columnWidth;
35670         var padavail = this.containerWidth - totalColWidth;
35671         // so for 2 columns - we need 3 'pads'
35672         
35673         var padNeeded = (1+this.cols) * this.padWidth;
35674         
35675         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35676         
35677         this.columnWidth += padExtra
35678         //this.padWidth = Math.floor(padavail /  ( this.cols));
35679         
35680         // adjust colum width so that padding is fixed??
35681         
35682         // we have 3 columns ... total = width * 3
35683         // we have X left over... that should be used by 
35684         
35685         //if (this.expandC) {
35686             
35687         //}
35688         
35689         
35690         
35691     },
35692     
35693     getContainerWidth : function()
35694     {
35695        /* // container is parent if fit width
35696         var container = this.isFitWidth ? this.element.parentNode : this.element;
35697         // check that this.size and size are there
35698         // IE8 triggers resize on body size change, so they might not be
35699         
35700         var size = getSize( container );  //FIXME
35701         this.containerWidth = size && size.innerWidth; //FIXME
35702         */
35703          
35704         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35705         
35706     },
35707     
35708     _getItemLayoutPosition : function( item )  // what is item?
35709     {
35710         // we resize the item to our columnWidth..
35711       
35712         item.setWidth(this.columnWidth);
35713         item.autoBoxAdjust  = false;
35714         
35715         var sz = item.getSize();
35716  
35717         // how many columns does this brick span
35718         var remainder = this.containerWidth % this.columnWidth;
35719         
35720         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35721         // round if off by 1 pixel, otherwise use ceil
35722         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35723         colSpan = Math.min( colSpan, this.cols );
35724         
35725         // normally this should be '1' as we dont' currently allow multi width columns..
35726         
35727         var colGroup = this._getColGroup( colSpan );
35728         // get the minimum Y value from the columns
35729         var minimumY = Math.min.apply( Math, colGroup );
35730         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35731         
35732         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35733          
35734         // position the brick
35735         var position = {
35736             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35737             y: this.currentSize.y + minimumY + this.padHeight
35738         };
35739         
35740         Roo.log(position);
35741         // apply setHeight to necessary columns
35742         var setHeight = minimumY + sz.height + this.padHeight;
35743         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35744         
35745         var setSpan = this.cols + 1 - colGroup.length;
35746         for ( var i = 0; i < setSpan; i++ ) {
35747           this.colYs[ shortColIndex + i ] = setHeight ;
35748         }
35749       
35750         return position;
35751     },
35752     
35753     /**
35754      * @param {Number} colSpan - number of columns the element spans
35755      * @returns {Array} colGroup
35756      */
35757     _getColGroup : function( colSpan )
35758     {
35759         if ( colSpan < 2 ) {
35760           // if brick spans only one column, use all the column Ys
35761           return this.colYs;
35762         }
35763       
35764         var colGroup = [];
35765         // how many different places could this brick fit horizontally
35766         var groupCount = this.cols + 1 - colSpan;
35767         // for each group potential horizontal position
35768         for ( var i = 0; i < groupCount; i++ ) {
35769           // make an array of colY values for that one group
35770           var groupColYs = this.colYs.slice( i, i + colSpan );
35771           // and get the max value of the array
35772           colGroup[i] = Math.max.apply( Math, groupColYs );
35773         }
35774         return colGroup;
35775     },
35776     /*
35777     _manageStamp : function( stamp )
35778     {
35779         var stampSize =  stamp.getSize();
35780         var offset = stamp.getBox();
35781         // get the columns that this stamp affects
35782         var firstX = this.isOriginLeft ? offset.x : offset.right;
35783         var lastX = firstX + stampSize.width;
35784         var firstCol = Math.floor( firstX / this.columnWidth );
35785         firstCol = Math.max( 0, firstCol );
35786         
35787         var lastCol = Math.floor( lastX / this.columnWidth );
35788         // lastCol should not go over if multiple of columnWidth #425
35789         lastCol -= lastX % this.columnWidth ? 0 : 1;
35790         lastCol = Math.min( this.cols - 1, lastCol );
35791         
35792         // set colYs to bottom of the stamp
35793         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35794             stampSize.height;
35795             
35796         for ( var i = firstCol; i <= lastCol; i++ ) {
35797           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35798         }
35799     },
35800     */
35801     
35802     _getContainerSize : function()
35803     {
35804         this.maxY = Math.max.apply( Math, this.colYs );
35805         var size = {
35806             height: this.maxY
35807         };
35808       
35809         if ( this.isFitWidth ) {
35810             size.width = this._getContainerFitWidth();
35811         }
35812       
35813         return size;
35814     },
35815     
35816     _getContainerFitWidth : function()
35817     {
35818         var unusedCols = 0;
35819         // count unused columns
35820         var i = this.cols;
35821         while ( --i ) {
35822           if ( this.colYs[i] !== 0 ) {
35823             break;
35824           }
35825           unusedCols++;
35826         }
35827         // fit container to columns that have been used
35828         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35829     },
35830     
35831     needsResizeLayout : function()
35832     {
35833         var previousWidth = this.containerWidth;
35834         this.getContainerWidth();
35835         return previousWidth !== this.containerWidth;
35836     }
35837  
35838 });
35839
35840  
35841
35842  /*
35843  * - LGPL
35844  *
35845  * element
35846  * 
35847  */
35848
35849 /**
35850  * @class Roo.bootstrap.MasonryBrick
35851  * @extends Roo.bootstrap.Component
35852  * Bootstrap MasonryBrick class
35853  * 
35854  * @constructor
35855  * Create a new MasonryBrick
35856  * @param {Object} config The config object
35857  */
35858
35859 Roo.bootstrap.MasonryBrick = function(config){
35860     
35861     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35862     
35863     Roo.bootstrap.MasonryBrick.register(this);
35864     
35865     this.addEvents({
35866         // raw events
35867         /**
35868          * @event click
35869          * When a MasonryBrick is clcik
35870          * @param {Roo.bootstrap.MasonryBrick} this
35871          * @param {Roo.EventObject} e
35872          */
35873         "click" : true
35874     });
35875 };
35876
35877 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35878     
35879     /**
35880      * @cfg {String} title
35881      */   
35882     title : '',
35883     /**
35884      * @cfg {String} html
35885      */   
35886     html : '',
35887     /**
35888      * @cfg {String} bgimage
35889      */   
35890     bgimage : '',
35891     /**
35892      * @cfg {String} videourl
35893      */   
35894     videourl : '',
35895     /**
35896      * @cfg {String} cls
35897      */   
35898     cls : '',
35899     /**
35900      * @cfg {String} href
35901      */   
35902     href : '',
35903     /**
35904      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35905      */   
35906     size : 'xs',
35907     
35908     /**
35909      * @cfg {String} placetitle (center|bottom)
35910      */   
35911     placetitle : '',
35912     
35913     /**
35914      * @cfg {Boolean} isFitContainer defalut true
35915      */   
35916     isFitContainer : true, 
35917     
35918     /**
35919      * @cfg {Boolean} preventDefault defalut false
35920      */   
35921     preventDefault : false, 
35922     
35923     /**
35924      * @cfg {Boolean} inverse defalut false
35925      */   
35926     maskInverse : false, 
35927     
35928     getAutoCreate : function()
35929     {
35930         if(!this.isFitContainer){
35931             return this.getSplitAutoCreate();
35932         }
35933         
35934         var cls = 'masonry-brick masonry-brick-full';
35935         
35936         if(this.href.length){
35937             cls += ' masonry-brick-link';
35938         }
35939         
35940         if(this.bgimage.length){
35941             cls += ' masonry-brick-image';
35942         }
35943         
35944         if(this.maskInverse){
35945             cls += ' mask-inverse';
35946         }
35947         
35948         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35949             cls += ' enable-mask';
35950         }
35951         
35952         if(this.size){
35953             cls += ' masonry-' + this.size + '-brick';
35954         }
35955         
35956         if(this.placetitle.length){
35957             
35958             switch (this.placetitle) {
35959                 case 'center' :
35960                     cls += ' masonry-center-title';
35961                     break;
35962                 case 'bottom' :
35963                     cls += ' masonry-bottom-title';
35964                     break;
35965                 default:
35966                     break;
35967             }
35968             
35969         } else {
35970             if(!this.html.length && !this.bgimage.length){
35971                 cls += ' masonry-center-title';
35972             }
35973
35974             if(!this.html.length && this.bgimage.length){
35975                 cls += ' masonry-bottom-title';
35976             }
35977         }
35978         
35979         if(this.cls){
35980             cls += ' ' + this.cls;
35981         }
35982         
35983         var cfg = {
35984             tag: (this.href.length) ? 'a' : 'div',
35985             cls: cls,
35986             cn: [
35987                 {
35988                     tag: 'div',
35989                     cls: 'masonry-brick-mask'
35990                 },
35991                 {
35992                     tag: 'div',
35993                     cls: 'masonry-brick-paragraph',
35994                     cn: []
35995                 }
35996             ]
35997         };
35998         
35999         if(this.href.length){
36000             cfg.href = this.href;
36001         }
36002         
36003         var cn = cfg.cn[1].cn;
36004         
36005         if(this.title.length){
36006             cn.push({
36007                 tag: 'h4',
36008                 cls: 'masonry-brick-title',
36009                 html: this.title
36010             });
36011         }
36012         
36013         if(this.html.length){
36014             cn.push({
36015                 tag: 'p',
36016                 cls: 'masonry-brick-text',
36017                 html: this.html
36018             });
36019         }
36020         
36021         if (!this.title.length && !this.html.length) {
36022             cfg.cn[1].cls += ' hide';
36023         }
36024         
36025         if(this.bgimage.length){
36026             cfg.cn.push({
36027                 tag: 'img',
36028                 cls: 'masonry-brick-image-view',
36029                 src: this.bgimage
36030             });
36031         }
36032         
36033         if(this.videourl.length){
36034             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36035             // youtube support only?
36036             cfg.cn.push({
36037                 tag: 'iframe',
36038                 cls: 'masonry-brick-image-view',
36039                 src: vurl,
36040                 frameborder : 0,
36041                 allowfullscreen : true
36042             });
36043         }
36044         
36045         return cfg;
36046         
36047     },
36048     
36049     getSplitAutoCreate : function()
36050     {
36051         var cls = 'masonry-brick masonry-brick-split';
36052         
36053         if(this.href.length){
36054             cls += ' masonry-brick-link';
36055         }
36056         
36057         if(this.bgimage.length){
36058             cls += ' masonry-brick-image';
36059         }
36060         
36061         if(this.size){
36062             cls += ' masonry-' + this.size + '-brick';
36063         }
36064         
36065         switch (this.placetitle) {
36066             case 'center' :
36067                 cls += ' masonry-center-title';
36068                 break;
36069             case 'bottom' :
36070                 cls += ' masonry-bottom-title';
36071                 break;
36072             default:
36073                 if(!this.bgimage.length){
36074                     cls += ' masonry-center-title';
36075                 }
36076
36077                 if(this.bgimage.length){
36078                     cls += ' masonry-bottom-title';
36079                 }
36080                 break;
36081         }
36082         
36083         if(this.cls){
36084             cls += ' ' + this.cls;
36085         }
36086         
36087         var cfg = {
36088             tag: (this.href.length) ? 'a' : 'div',
36089             cls: cls,
36090             cn: [
36091                 {
36092                     tag: 'div',
36093                     cls: 'masonry-brick-split-head',
36094                     cn: [
36095                         {
36096                             tag: 'div',
36097                             cls: 'masonry-brick-paragraph',
36098                             cn: []
36099                         }
36100                     ]
36101                 },
36102                 {
36103                     tag: 'div',
36104                     cls: 'masonry-brick-split-body',
36105                     cn: []
36106                 }
36107             ]
36108         };
36109         
36110         if(this.href.length){
36111             cfg.href = this.href;
36112         }
36113         
36114         if(this.title.length){
36115             cfg.cn[0].cn[0].cn.push({
36116                 tag: 'h4',
36117                 cls: 'masonry-brick-title',
36118                 html: this.title
36119             });
36120         }
36121         
36122         if(this.html.length){
36123             cfg.cn[1].cn.push({
36124                 tag: 'p',
36125                 cls: 'masonry-brick-text',
36126                 html: this.html
36127             });
36128         }
36129
36130         if(this.bgimage.length){
36131             cfg.cn[0].cn.push({
36132                 tag: 'img',
36133                 cls: 'masonry-brick-image-view',
36134                 src: this.bgimage
36135             });
36136         }
36137         
36138         if(this.videourl.length){
36139             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36140             // youtube support only?
36141             cfg.cn[0].cn.cn.push({
36142                 tag: 'iframe',
36143                 cls: 'masonry-brick-image-view',
36144                 src: vurl,
36145                 frameborder : 0,
36146                 allowfullscreen : true
36147             });
36148         }
36149         
36150         return cfg;
36151     },
36152     
36153     initEvents: function() 
36154     {
36155         switch (this.size) {
36156             case 'xs' :
36157                 this.x = 1;
36158                 this.y = 1;
36159                 break;
36160             case 'sm' :
36161                 this.x = 2;
36162                 this.y = 2;
36163                 break;
36164             case 'md' :
36165             case 'md-left' :
36166             case 'md-right' :
36167                 this.x = 3;
36168                 this.y = 3;
36169                 break;
36170             case 'tall' :
36171                 this.x = 2;
36172                 this.y = 3;
36173                 break;
36174             case 'wide' :
36175                 this.x = 3;
36176                 this.y = 2;
36177                 break;
36178             case 'wide-thin' :
36179                 this.x = 3;
36180                 this.y = 1;
36181                 break;
36182                         
36183             default :
36184                 break;
36185         }
36186         
36187         if(Roo.isTouch){
36188             this.el.on('touchstart', this.onTouchStart, this);
36189             this.el.on('touchmove', this.onTouchMove, this);
36190             this.el.on('touchend', this.onTouchEnd, this);
36191             this.el.on('contextmenu', this.onContextMenu, this);
36192         } else {
36193             this.el.on('mouseenter'  ,this.enter, this);
36194             this.el.on('mouseleave', this.leave, this);
36195             this.el.on('click', this.onClick, this);
36196         }
36197         
36198         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36199             this.parent().bricks.push(this);   
36200         }
36201         
36202     },
36203     
36204     onClick: function(e, el)
36205     {
36206         var time = this.endTimer - this.startTimer;
36207         // Roo.log(e.preventDefault());
36208         if(Roo.isTouch){
36209             if(time > 1000){
36210                 e.preventDefault();
36211                 return;
36212             }
36213         }
36214         
36215         if(!this.preventDefault){
36216             return;
36217         }
36218         
36219         e.preventDefault();
36220         
36221         if (this.activeClass != '') {
36222             this.selectBrick();
36223         }
36224         
36225         this.fireEvent('click', this, e);
36226     },
36227     
36228     enter: function(e, el)
36229     {
36230         e.preventDefault();
36231         
36232         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36233             return;
36234         }
36235         
36236         if(this.bgimage.length && this.html.length){
36237             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36238         }
36239     },
36240     
36241     leave: function(e, el)
36242     {
36243         e.preventDefault();
36244         
36245         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36246             return;
36247         }
36248         
36249         if(this.bgimage.length && this.html.length){
36250             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36251         }
36252     },
36253     
36254     onTouchStart: function(e, el)
36255     {
36256 //        e.preventDefault();
36257         
36258         this.touchmoved = false;
36259         
36260         if(!this.isFitContainer){
36261             return;
36262         }
36263         
36264         if(!this.bgimage.length || !this.html.length){
36265             return;
36266         }
36267         
36268         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36269         
36270         this.timer = new Date().getTime();
36271         
36272     },
36273     
36274     onTouchMove: function(e, el)
36275     {
36276         this.touchmoved = true;
36277     },
36278     
36279     onContextMenu : function(e,el)
36280     {
36281         e.preventDefault();
36282         e.stopPropagation();
36283         return false;
36284     },
36285     
36286     onTouchEnd: function(e, el)
36287     {
36288 //        e.preventDefault();
36289         
36290         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36291         
36292             this.leave(e,el);
36293             
36294             return;
36295         }
36296         
36297         if(!this.bgimage.length || !this.html.length){
36298             
36299             if(this.href.length){
36300                 window.location.href = this.href;
36301             }
36302             
36303             return;
36304         }
36305         
36306         if(!this.isFitContainer){
36307             return;
36308         }
36309         
36310         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36311         
36312         window.location.href = this.href;
36313     },
36314     
36315     //selection on single brick only
36316     selectBrick : function() {
36317         
36318         if (!this.parentId) {
36319             return;
36320         }
36321         
36322         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36323         var index = m.selectedBrick.indexOf(this.id);
36324         
36325         if ( index > -1) {
36326             m.selectedBrick.splice(index,1);
36327             this.el.removeClass(this.activeClass);
36328             return;
36329         }
36330         
36331         for(var i = 0; i < m.selectedBrick.length; i++) {
36332             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36333             b.el.removeClass(b.activeClass);
36334         }
36335         
36336         m.selectedBrick = [];
36337         
36338         m.selectedBrick.push(this.id);
36339         this.el.addClass(this.activeClass);
36340         return;
36341     },
36342     
36343     isSelected : function(){
36344         return this.el.hasClass(this.activeClass);
36345         
36346     }
36347 });
36348
36349 Roo.apply(Roo.bootstrap.MasonryBrick, {
36350     
36351     //groups: {},
36352     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36353      /**
36354     * register a Masonry Brick
36355     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36356     */
36357     
36358     register : function(brick)
36359     {
36360         //this.groups[brick.id] = brick;
36361         this.groups.add(brick.id, brick);
36362     },
36363     /**
36364     * fetch a  masonry brick based on the masonry brick ID
36365     * @param {string} the masonry brick to add
36366     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36367     */
36368     
36369     get: function(brick_id) 
36370     {
36371         // if (typeof(this.groups[brick_id]) == 'undefined') {
36372         //     return false;
36373         // }
36374         // return this.groups[brick_id] ;
36375         
36376         if(this.groups.key(brick_id)) {
36377             return this.groups.key(brick_id);
36378         }
36379         
36380         return false;
36381     }
36382     
36383     
36384     
36385 });
36386
36387  /*
36388  * - LGPL
36389  *
36390  * element
36391  * 
36392  */
36393
36394 /**
36395  * @class Roo.bootstrap.Brick
36396  * @extends Roo.bootstrap.Component
36397  * Bootstrap Brick class
36398  * 
36399  * @constructor
36400  * Create a new Brick
36401  * @param {Object} config The config object
36402  */
36403
36404 Roo.bootstrap.Brick = function(config){
36405     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36406     
36407     this.addEvents({
36408         // raw events
36409         /**
36410          * @event click
36411          * When a Brick is click
36412          * @param {Roo.bootstrap.Brick} this
36413          * @param {Roo.EventObject} e
36414          */
36415         "click" : true
36416     });
36417 };
36418
36419 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36420     
36421     /**
36422      * @cfg {String} title
36423      */   
36424     title : '',
36425     /**
36426      * @cfg {String} html
36427      */   
36428     html : '',
36429     /**
36430      * @cfg {String} bgimage
36431      */   
36432     bgimage : '',
36433     /**
36434      * @cfg {String} cls
36435      */   
36436     cls : '',
36437     /**
36438      * @cfg {String} href
36439      */   
36440     href : '',
36441     /**
36442      * @cfg {String} video
36443      */   
36444     video : '',
36445     /**
36446      * @cfg {Boolean} square
36447      */   
36448     square : true,
36449     
36450     getAutoCreate : function()
36451     {
36452         var cls = 'roo-brick';
36453         
36454         if(this.href.length){
36455             cls += ' roo-brick-link';
36456         }
36457         
36458         if(this.bgimage.length){
36459             cls += ' roo-brick-image';
36460         }
36461         
36462         if(!this.html.length && !this.bgimage.length){
36463             cls += ' roo-brick-center-title';
36464         }
36465         
36466         if(!this.html.length && this.bgimage.length){
36467             cls += ' roo-brick-bottom-title';
36468         }
36469         
36470         if(this.cls){
36471             cls += ' ' + this.cls;
36472         }
36473         
36474         var cfg = {
36475             tag: (this.href.length) ? 'a' : 'div',
36476             cls: cls,
36477             cn: [
36478                 {
36479                     tag: 'div',
36480                     cls: 'roo-brick-paragraph',
36481                     cn: []
36482                 }
36483             ]
36484         };
36485         
36486         if(this.href.length){
36487             cfg.href = this.href;
36488         }
36489         
36490         var cn = cfg.cn[0].cn;
36491         
36492         if(this.title.length){
36493             cn.push({
36494                 tag: 'h4',
36495                 cls: 'roo-brick-title',
36496                 html: this.title
36497             });
36498         }
36499         
36500         if(this.html.length){
36501             cn.push({
36502                 tag: 'p',
36503                 cls: 'roo-brick-text',
36504                 html: this.html
36505             });
36506         } else {
36507             cn.cls += ' hide';
36508         }
36509         
36510         if(this.bgimage.length){
36511             cfg.cn.push({
36512                 tag: 'img',
36513                 cls: 'roo-brick-image-view',
36514                 src: this.bgimage
36515             });
36516         }
36517         
36518         return cfg;
36519     },
36520     
36521     initEvents: function() 
36522     {
36523         if(this.title.length || this.html.length){
36524             this.el.on('mouseenter'  ,this.enter, this);
36525             this.el.on('mouseleave', this.leave, this);
36526         }
36527         
36528         Roo.EventManager.onWindowResize(this.resize, this); 
36529         
36530         if(this.bgimage.length){
36531             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36532             this.imageEl.on('load', this.onImageLoad, this);
36533             return;
36534         }
36535         
36536         this.resize();
36537     },
36538     
36539     onImageLoad : function()
36540     {
36541         this.resize();
36542     },
36543     
36544     resize : function()
36545     {
36546         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36547         
36548         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36549         
36550         if(this.bgimage.length){
36551             var image = this.el.select('.roo-brick-image-view', true).first();
36552             
36553             image.setWidth(paragraph.getWidth());
36554             
36555             if(this.square){
36556                 image.setHeight(paragraph.getWidth());
36557             }
36558             
36559             this.el.setHeight(image.getHeight());
36560             paragraph.setHeight(image.getHeight());
36561             
36562         }
36563         
36564     },
36565     
36566     enter: function(e, el)
36567     {
36568         e.preventDefault();
36569         
36570         if(this.bgimage.length){
36571             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36572             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36573         }
36574     },
36575     
36576     leave: function(e, el)
36577     {
36578         e.preventDefault();
36579         
36580         if(this.bgimage.length){
36581             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36582             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36583         }
36584     }
36585     
36586 });
36587
36588  
36589
36590  /*
36591  * - LGPL
36592  *
36593  * Number field 
36594  */
36595
36596 /**
36597  * @class Roo.bootstrap.form.NumberField
36598  * @extends Roo.bootstrap.form.Input
36599  * Bootstrap NumberField class
36600  * 
36601  * 
36602  * 
36603  * 
36604  * @constructor
36605  * Create a new NumberField
36606  * @param {Object} config The config object
36607  */
36608
36609 Roo.bootstrap.form.NumberField = function(config){
36610     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36611 };
36612
36613 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36614     
36615     /**
36616      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36617      */
36618     allowDecimals : true,
36619     /**
36620      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36621      */
36622     decimalSeparator : ".",
36623     /**
36624      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36625      */
36626     decimalPrecision : 2,
36627     /**
36628      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36629      */
36630     allowNegative : true,
36631     
36632     /**
36633      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36634      */
36635     allowZero: true,
36636     /**
36637      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36638      */
36639     minValue : Number.NEGATIVE_INFINITY,
36640     /**
36641      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36642      */
36643     maxValue : Number.MAX_VALUE,
36644     /**
36645      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36646      */
36647     minText : "The minimum value for this field is {0}",
36648     /**
36649      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36650      */
36651     maxText : "The maximum value for this field is {0}",
36652     /**
36653      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36654      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36655      */
36656     nanText : "{0} is not a valid number",
36657     /**
36658      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36659      */
36660     thousandsDelimiter : false,
36661     /**
36662      * @cfg {String} valueAlign alignment of value
36663      */
36664     valueAlign : "left",
36665
36666     getAutoCreate : function()
36667     {
36668         var hiddenInput = {
36669             tag: 'input',
36670             type: 'hidden',
36671             id: Roo.id(),
36672             cls: 'hidden-number-input'
36673         };
36674         
36675         if (this.name) {
36676             hiddenInput.name = this.name;
36677         }
36678         
36679         this.name = '';
36680         
36681         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36682         
36683         this.name = hiddenInput.name;
36684         
36685         if(cfg.cn.length > 0) {
36686             cfg.cn.push(hiddenInput);
36687         }
36688         
36689         return cfg;
36690     },
36691
36692     // private
36693     initEvents : function()
36694     {   
36695         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36696         
36697         var allowed = "0123456789";
36698         
36699         if(this.allowDecimals){
36700             allowed += this.decimalSeparator;
36701         }
36702         
36703         if(this.allowNegative){
36704             allowed += "-";
36705         }
36706         
36707         if(this.thousandsDelimiter) {
36708             allowed += ",";
36709         }
36710         
36711         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36712         
36713         var keyPress = function(e){
36714             
36715             var k = e.getKey();
36716             
36717             var c = e.getCharCode();
36718             
36719             if(
36720                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36721                     allowed.indexOf(String.fromCharCode(c)) === -1
36722             ){
36723                 e.stopEvent();
36724                 return;
36725             }
36726             
36727             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36728                 return;
36729             }
36730             
36731             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36732                 e.stopEvent();
36733             }
36734         };
36735         
36736         this.el.on("keypress", keyPress, this);
36737     },
36738     
36739     validateValue : function(value)
36740     {
36741         
36742         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36743             return false;
36744         }
36745         
36746         var num = this.parseValue(value);
36747         
36748         if(isNaN(num)){
36749             this.markInvalid(String.format(this.nanText, value));
36750             return false;
36751         }
36752         
36753         if(num < this.minValue){
36754             this.markInvalid(String.format(this.minText, this.minValue));
36755             return false;
36756         }
36757         
36758         if(num > this.maxValue){
36759             this.markInvalid(String.format(this.maxText, this.maxValue));
36760             return false;
36761         }
36762         
36763         return true;
36764     },
36765
36766     getValue : function()
36767     {
36768         var v = this.hiddenEl().getValue();
36769         
36770         return this.fixPrecision(this.parseValue(v));
36771     },
36772
36773     parseValue : function(value)
36774     {
36775         if(this.thousandsDelimiter) {
36776             value += "";
36777             r = new RegExp(",", "g");
36778             value = value.replace(r, "");
36779         }
36780         
36781         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36782         return isNaN(value) ? '' : value;
36783     },
36784
36785     fixPrecision : function(value)
36786     {
36787         if(this.thousandsDelimiter) {
36788             value += "";
36789             r = new RegExp(",", "g");
36790             value = value.replace(r, "");
36791         }
36792         
36793         var nan = isNaN(value);
36794         
36795         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36796             return nan ? '' : value;
36797         }
36798         return parseFloat(value).toFixed(this.decimalPrecision);
36799     },
36800
36801     setValue : function(v)
36802     {
36803         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36804         
36805         this.value = v;
36806         
36807         if(this.rendered){
36808             
36809             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36810             
36811             this.inputEl().dom.value = (v == '') ? '' :
36812                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36813             
36814             if(!this.allowZero && v === '0') {
36815                 this.hiddenEl().dom.value = '';
36816                 this.inputEl().dom.value = '';
36817             }
36818             
36819             this.validate();
36820         }
36821     },
36822
36823     decimalPrecisionFcn : function(v)
36824     {
36825         return Math.floor(v);
36826     },
36827
36828     beforeBlur : function()
36829     {
36830         var v = this.parseValue(this.getRawValue());
36831         
36832         if(v || v === 0 || v === ''){
36833             this.setValue(v);
36834         }
36835     },
36836     
36837     hiddenEl : function()
36838     {
36839         return this.el.select('input.hidden-number-input',true).first();
36840     }
36841     
36842 });
36843
36844  
36845
36846 /*
36847 * Licence: LGPL
36848 */
36849
36850 /**
36851  * @class Roo.bootstrap.DocumentSlider
36852  * @extends Roo.bootstrap.Component
36853  * Bootstrap DocumentSlider class
36854  * 
36855  * @constructor
36856  * Create a new DocumentViewer
36857  * @param {Object} config The config object
36858  */
36859
36860 Roo.bootstrap.DocumentSlider = function(config){
36861     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36862     
36863     this.files = [];
36864     
36865     this.addEvents({
36866         /**
36867          * @event initial
36868          * Fire after initEvent
36869          * @param {Roo.bootstrap.DocumentSlider} this
36870          */
36871         "initial" : true,
36872         /**
36873          * @event update
36874          * Fire after update
36875          * @param {Roo.bootstrap.DocumentSlider} this
36876          */
36877         "update" : true,
36878         /**
36879          * @event click
36880          * Fire after click
36881          * @param {Roo.bootstrap.DocumentSlider} this
36882          */
36883         "click" : true
36884     });
36885 };
36886
36887 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36888     
36889     files : false,
36890     
36891     indicator : 0,
36892     
36893     getAutoCreate : function()
36894     {
36895         var cfg = {
36896             tag : 'div',
36897             cls : 'roo-document-slider',
36898             cn : [
36899                 {
36900                     tag : 'div',
36901                     cls : 'roo-document-slider-header',
36902                     cn : [
36903                         {
36904                             tag : 'div',
36905                             cls : 'roo-document-slider-header-title'
36906                         }
36907                     ]
36908                 },
36909                 {
36910                     tag : 'div',
36911                     cls : 'roo-document-slider-body',
36912                     cn : [
36913                         {
36914                             tag : 'div',
36915                             cls : 'roo-document-slider-prev',
36916                             cn : [
36917                                 {
36918                                     tag : 'i',
36919                                     cls : 'fa fa-chevron-left'
36920                                 }
36921                             ]
36922                         },
36923                         {
36924                             tag : 'div',
36925                             cls : 'roo-document-slider-thumb',
36926                             cn : [
36927                                 {
36928                                     tag : 'img',
36929                                     cls : 'roo-document-slider-image'
36930                                 }
36931                             ]
36932                         },
36933                         {
36934                             tag : 'div',
36935                             cls : 'roo-document-slider-next',
36936                             cn : [
36937                                 {
36938                                     tag : 'i',
36939                                     cls : 'fa fa-chevron-right'
36940                                 }
36941                             ]
36942                         }
36943                     ]
36944                 }
36945             ]
36946         };
36947         
36948         return cfg;
36949     },
36950     
36951     initEvents : function()
36952     {
36953         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36954         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36955         
36956         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36957         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36958         
36959         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36960         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36961         
36962         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36963         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36964         
36965         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36966         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36967         
36968         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36969         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36970         
36971         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36972         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36973         
36974         this.thumbEl.on('click', this.onClick, this);
36975         
36976         this.prevIndicator.on('click', this.prev, this);
36977         
36978         this.nextIndicator.on('click', this.next, this);
36979         
36980     },
36981     
36982     initial : function()
36983     {
36984         if(this.files.length){
36985             this.indicator = 1;
36986             this.update()
36987         }
36988         
36989         this.fireEvent('initial', this);
36990     },
36991     
36992     update : function()
36993     {
36994         this.imageEl.attr('src', this.files[this.indicator - 1]);
36995         
36996         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36997         
36998         this.prevIndicator.show();
36999         
37000         if(this.indicator == 1){
37001             this.prevIndicator.hide();
37002         }
37003         
37004         this.nextIndicator.show();
37005         
37006         if(this.indicator == this.files.length){
37007             this.nextIndicator.hide();
37008         }
37009         
37010         this.thumbEl.scrollTo('top');
37011         
37012         this.fireEvent('update', this);
37013     },
37014     
37015     onClick : function(e)
37016     {
37017         e.preventDefault();
37018         
37019         this.fireEvent('click', this);
37020     },
37021     
37022     prev : function(e)
37023     {
37024         e.preventDefault();
37025         
37026         this.indicator = Math.max(1, this.indicator - 1);
37027         
37028         this.update();
37029     },
37030     
37031     next : function(e)
37032     {
37033         e.preventDefault();
37034         
37035         this.indicator = Math.min(this.files.length, this.indicator + 1);
37036         
37037         this.update();
37038     }
37039 });
37040 /*
37041  * - LGPL
37042  *
37043  * RadioSet
37044  *
37045  *
37046  */
37047
37048 /**
37049  * @class Roo.bootstrap.form.RadioSet
37050  * @extends Roo.bootstrap.form.Input
37051  * @children Roo.bootstrap.form.Radio
37052  * Bootstrap RadioSet class
37053  * @cfg {String} indicatorpos (left|right) default left
37054  * @cfg {Boolean} inline (true|false) inline the element (default true)
37055  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37056  * @constructor
37057  * Create a new RadioSet
37058  * @param {Object} config The config object
37059  */
37060
37061 Roo.bootstrap.form.RadioSet = function(config){
37062     
37063     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37064     
37065     this.radioes = [];
37066     
37067     Roo.bootstrap.form.RadioSet.register(this);
37068     
37069     this.addEvents({
37070         /**
37071         * @event check
37072         * Fires when the element is checked or unchecked.
37073         * @param {Roo.bootstrap.form.RadioSet} this This radio
37074         * @param {Roo.bootstrap.form.Radio} item The checked item
37075         */
37076        check : true,
37077        /**
37078         * @event click
37079         * Fires when the element is click.
37080         * @param {Roo.bootstrap.form.RadioSet} this This radio set
37081         * @param {Roo.bootstrap.form.Radio} item The checked item
37082         * @param {Roo.EventObject} e The event object
37083         */
37084        click : true
37085     });
37086     
37087 };
37088
37089 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
37090
37091     radioes : false,
37092     
37093     inline : true,
37094     
37095     weight : '',
37096     
37097     indicatorpos : 'left',
37098     
37099     getAutoCreate : function()
37100     {
37101         var label = {
37102             tag : 'label',
37103             cls : 'roo-radio-set-label',
37104             cn : [
37105                 {
37106                     tag : 'span',
37107                     html : this.fieldLabel
37108                 }
37109             ]
37110         };
37111         if (Roo.bootstrap.version == 3) {
37112             
37113             
37114             if(this.indicatorpos == 'left'){
37115                 label.cn.unshift({
37116                     tag : 'i',
37117                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37118                     tooltip : 'This field is required'
37119                 });
37120             } else {
37121                 label.cn.push({
37122                     tag : 'i',
37123                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37124                     tooltip : 'This field is required'
37125                 });
37126             }
37127         }
37128         var items = {
37129             tag : 'div',
37130             cls : 'roo-radio-set-items'
37131         };
37132         
37133         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37134         
37135         if (align === 'left' && this.fieldLabel.length) {
37136             
37137             items = {
37138                 cls : "roo-radio-set-right", 
37139                 cn: [
37140                     items
37141                 ]
37142             };
37143             
37144             if(this.labelWidth > 12){
37145                 label.style = "width: " + this.labelWidth + 'px';
37146             }
37147             
37148             if(this.labelWidth < 13 && this.labelmd == 0){
37149                 this.labelmd = this.labelWidth;
37150             }
37151             
37152             if(this.labellg > 0){
37153                 label.cls += ' col-lg-' + this.labellg;
37154                 items.cls += ' col-lg-' + (12 - this.labellg);
37155             }
37156             
37157             if(this.labelmd > 0){
37158                 label.cls += ' col-md-' + this.labelmd;
37159                 items.cls += ' col-md-' + (12 - this.labelmd);
37160             }
37161             
37162             if(this.labelsm > 0){
37163                 label.cls += ' col-sm-' + this.labelsm;
37164                 items.cls += ' col-sm-' + (12 - this.labelsm);
37165             }
37166             
37167             if(this.labelxs > 0){
37168                 label.cls += ' col-xs-' + this.labelxs;
37169                 items.cls += ' col-xs-' + (12 - this.labelxs);
37170             }
37171         }
37172         
37173         var cfg = {
37174             tag : 'div',
37175             cls : 'roo-radio-set',
37176             cn : [
37177                 {
37178                     tag : 'input',
37179                     cls : 'roo-radio-set-input',
37180                     type : 'hidden',
37181                     name : this.name,
37182                     value : this.value ? this.value :  ''
37183                 },
37184                 label,
37185                 items
37186             ]
37187         };
37188         
37189         if(this.weight.length){
37190             cfg.cls += ' roo-radio-' + this.weight;
37191         }
37192         
37193         if(this.inline) {
37194             cfg.cls += ' roo-radio-set-inline';
37195         }
37196         
37197         var settings=this;
37198         ['xs','sm','md','lg'].map(function(size){
37199             if (settings[size]) {
37200                 cfg.cls += ' col-' + size + '-' + settings[size];
37201             }
37202         });
37203         
37204         return cfg;
37205         
37206     },
37207
37208     initEvents : function()
37209     {
37210         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37211         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37212         
37213         if(!this.fieldLabel.length){
37214             this.labelEl.hide();
37215         }
37216         
37217         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37218         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37219         
37220         this.indicator = this.indicatorEl();
37221         
37222         if(this.indicator){
37223             this.indicator.addClass('invisible');
37224         }
37225         
37226         this.originalValue = this.getValue();
37227         
37228     },
37229     
37230     inputEl: function ()
37231     {
37232         return this.el.select('.roo-radio-set-input', true).first();
37233     },
37234     
37235     getChildContainer : function()
37236     {
37237         return this.itemsEl;
37238     },
37239     
37240     register : function(item)
37241     {
37242         this.radioes.push(item);
37243         
37244     },
37245     
37246     validate : function()
37247     {   
37248         if(this.getVisibilityEl().hasClass('hidden')){
37249             return true;
37250         }
37251         
37252         var valid = false;
37253         
37254         Roo.each(this.radioes, function(i){
37255             if(!i.checked){
37256                 return;
37257             }
37258             
37259             valid = true;
37260             return false;
37261         });
37262         
37263         if(this.allowBlank) {
37264             return true;
37265         }
37266         
37267         if(this.disabled || valid){
37268             this.markValid();
37269             return true;
37270         }
37271         
37272         this.markInvalid();
37273         return false;
37274         
37275     },
37276     
37277     markValid : function()
37278     {
37279         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37280             this.indicatorEl().removeClass('visible');
37281             this.indicatorEl().addClass('invisible');
37282         }
37283         
37284         
37285         if (Roo.bootstrap.version == 3) {
37286             this.el.removeClass([this.invalidClass, this.validClass]);
37287             this.el.addClass(this.validClass);
37288         } else {
37289             this.el.removeClass(['is-invalid','is-valid']);
37290             this.el.addClass(['is-valid']);
37291         }
37292         this.fireEvent('valid', this);
37293     },
37294     
37295     markInvalid : function(msg)
37296     {
37297         if(this.allowBlank || this.disabled){
37298             return;
37299         }
37300         
37301         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37302             this.indicatorEl().removeClass('invisible');
37303             this.indicatorEl().addClass('visible');
37304         }
37305         if (Roo.bootstrap.version == 3) {
37306             this.el.removeClass([this.invalidClass, this.validClass]);
37307             this.el.addClass(this.invalidClass);
37308         } else {
37309             this.el.removeClass(['is-invalid','is-valid']);
37310             this.el.addClass(['is-invalid']);
37311         }
37312         
37313         this.fireEvent('invalid', this, msg);
37314         
37315     },
37316     
37317     setValue : function(v, suppressEvent)
37318     {   
37319         if(this.value === v){
37320             return;
37321         }
37322         
37323         this.value = v;
37324         
37325         if(this.rendered){
37326             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37327         }
37328         
37329         Roo.each(this.radioes, function(i){
37330             i.checked = false;
37331             i.el.removeClass('checked');
37332         });
37333         
37334         Roo.each(this.radioes, function(i){
37335             
37336             if(i.value === v || i.value.toString() === v.toString()){
37337                 i.checked = true;
37338                 i.el.addClass('checked');
37339                 
37340                 if(suppressEvent !== true){
37341                     this.fireEvent('check', this, i);
37342                 }
37343                 
37344                 return false;
37345             }
37346             
37347         }, this);
37348         
37349         this.validate();
37350     },
37351     
37352     clearInvalid : function(){
37353         
37354         if(!this.el || this.preventMark){
37355             return;
37356         }
37357         
37358         this.el.removeClass([this.invalidClass]);
37359         
37360         this.fireEvent('valid', this);
37361     }
37362     
37363 });
37364
37365 Roo.apply(Roo.bootstrap.form.RadioSet, {
37366     
37367     groups: {},
37368     
37369     register : function(set)
37370     {
37371         this.groups[set.name] = set;
37372     },
37373     
37374     get: function(name) 
37375     {
37376         if (typeof(this.groups[name]) == 'undefined') {
37377             return false;
37378         }
37379         
37380         return this.groups[name] ;
37381     }
37382     
37383 });
37384 /*
37385  * Based on:
37386  * Ext JS Library 1.1.1
37387  * Copyright(c) 2006-2007, Ext JS, LLC.
37388  *
37389  * Originally Released Under LGPL - original licence link has changed is not relivant.
37390  *
37391  * Fork - LGPL
37392  * <script type="text/javascript">
37393  */
37394
37395
37396 /**
37397  * @class Roo.bootstrap.SplitBar
37398  * @extends Roo.util.Observable
37399  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37400  * <br><br>
37401  * Usage:
37402  * <pre><code>
37403 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37404                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37405 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37406 split.minSize = 100;
37407 split.maxSize = 600;
37408 split.animate = true;
37409 split.on('moved', splitterMoved);
37410 </code></pre>
37411  * @constructor
37412  * Create a new SplitBar
37413  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37414  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37415  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37416  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37417                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37418                         position of the SplitBar).
37419  */
37420 Roo.bootstrap.SplitBar = function(cfg){
37421     
37422     /** @private */
37423     
37424     //{
37425     //  dragElement : elm
37426     //  resizingElement: el,
37427         // optional..
37428     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37429     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37430         // existingProxy ???
37431     //}
37432     
37433     this.el = Roo.get(cfg.dragElement, true);
37434     this.el.dom.unselectable = "on";
37435     /** @private */
37436     this.resizingEl = Roo.get(cfg.resizingElement, true);
37437
37438     /**
37439      * @private
37440      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37441      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37442      * @type Number
37443      */
37444     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37445     
37446     /**
37447      * The minimum size of the resizing element. (Defaults to 0)
37448      * @type Number
37449      */
37450     this.minSize = 0;
37451     
37452     /**
37453      * The maximum size of the resizing element. (Defaults to 2000)
37454      * @type Number
37455      */
37456     this.maxSize = 2000;
37457     
37458     /**
37459      * Whether to animate the transition to the new size
37460      * @type Boolean
37461      */
37462     this.animate = false;
37463     
37464     /**
37465      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37466      * @type Boolean
37467      */
37468     this.useShim = false;
37469     
37470     /** @private */
37471     this.shim = null;
37472     
37473     if(!cfg.existingProxy){
37474         /** @private */
37475         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37476     }else{
37477         this.proxy = Roo.get(cfg.existingProxy).dom;
37478     }
37479     /** @private */
37480     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37481     
37482     /** @private */
37483     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37484     
37485     /** @private */
37486     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37487     
37488     /** @private */
37489     this.dragSpecs = {};
37490     
37491     /**
37492      * @private The adapter to use to positon and resize elements
37493      */
37494     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37495     this.adapter.init(this);
37496     
37497     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37498         /** @private */
37499         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37500         this.el.addClass("roo-splitbar-h");
37501     }else{
37502         /** @private */
37503         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37504         this.el.addClass("roo-splitbar-v");
37505     }
37506     
37507     this.addEvents({
37508         /**
37509          * @event resize
37510          * Fires when the splitter is moved (alias for {@link #event-moved})
37511          * @param {Roo.bootstrap.SplitBar} this
37512          * @param {Number} newSize the new width or height
37513          */
37514         "resize" : true,
37515         /**
37516          * @event moved
37517          * Fires when the splitter is moved
37518          * @param {Roo.bootstrap.SplitBar} this
37519          * @param {Number} newSize the new width or height
37520          */
37521         "moved" : true,
37522         /**
37523          * @event beforeresize
37524          * Fires before the splitter is dragged
37525          * @param {Roo.bootstrap.SplitBar} this
37526          */
37527         "beforeresize" : true,
37528
37529         "beforeapply" : true
37530     });
37531
37532     Roo.util.Observable.call(this);
37533 };
37534
37535 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37536     onStartProxyDrag : function(x, y){
37537         this.fireEvent("beforeresize", this);
37538         if(!this.overlay){
37539             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37540             o.unselectable();
37541             o.enableDisplayMode("block");
37542             // all splitbars share the same overlay
37543             Roo.bootstrap.SplitBar.prototype.overlay = o;
37544         }
37545         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37546         this.overlay.show();
37547         Roo.get(this.proxy).setDisplayed("block");
37548         var size = this.adapter.getElementSize(this);
37549         this.activeMinSize = this.getMinimumSize();;
37550         this.activeMaxSize = this.getMaximumSize();;
37551         var c1 = size - this.activeMinSize;
37552         var c2 = Math.max(this.activeMaxSize - size, 0);
37553         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37554             this.dd.resetConstraints();
37555             this.dd.setXConstraint(
37556                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37557                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37558             );
37559             this.dd.setYConstraint(0, 0);
37560         }else{
37561             this.dd.resetConstraints();
37562             this.dd.setXConstraint(0, 0);
37563             this.dd.setYConstraint(
37564                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37565                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37566             );
37567          }
37568         this.dragSpecs.startSize = size;
37569         this.dragSpecs.startPoint = [x, y];
37570         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37571     },
37572     
37573     /** 
37574      * @private Called after the drag operation by the DDProxy
37575      */
37576     onEndProxyDrag : function(e){
37577         Roo.get(this.proxy).setDisplayed(false);
37578         var endPoint = Roo.lib.Event.getXY(e);
37579         if(this.overlay){
37580             this.overlay.hide();
37581         }
37582         var newSize;
37583         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37584             newSize = this.dragSpecs.startSize + 
37585                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37586                     endPoint[0] - this.dragSpecs.startPoint[0] :
37587                     this.dragSpecs.startPoint[0] - endPoint[0]
37588                 );
37589         }else{
37590             newSize = this.dragSpecs.startSize + 
37591                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37592                     endPoint[1] - this.dragSpecs.startPoint[1] :
37593                     this.dragSpecs.startPoint[1] - endPoint[1]
37594                 );
37595         }
37596         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37597         if(newSize != this.dragSpecs.startSize){
37598             if(this.fireEvent('beforeapply', this, newSize) !== false){
37599                 this.adapter.setElementSize(this, newSize);
37600                 this.fireEvent("moved", this, newSize);
37601                 this.fireEvent("resize", this, newSize);
37602             }
37603         }
37604     },
37605     
37606     /**
37607      * Get the adapter this SplitBar uses
37608      * @return The adapter object
37609      */
37610     getAdapter : function(){
37611         return this.adapter;
37612     },
37613     
37614     /**
37615      * Set the adapter this SplitBar uses
37616      * @param {Object} adapter A SplitBar adapter object
37617      */
37618     setAdapter : function(adapter){
37619         this.adapter = adapter;
37620         this.adapter.init(this);
37621     },
37622     
37623     /**
37624      * Gets the minimum size for the resizing element
37625      * @return {Number} The minimum size
37626      */
37627     getMinimumSize : function(){
37628         return this.minSize;
37629     },
37630     
37631     /**
37632      * Sets the minimum size for the resizing element
37633      * @param {Number} minSize The minimum size
37634      */
37635     setMinimumSize : function(minSize){
37636         this.minSize = minSize;
37637     },
37638     
37639     /**
37640      * Gets the maximum size for the resizing element
37641      * @return {Number} The maximum size
37642      */
37643     getMaximumSize : function(){
37644         return this.maxSize;
37645     },
37646     
37647     /**
37648      * Sets the maximum size for the resizing element
37649      * @param {Number} maxSize The maximum size
37650      */
37651     setMaximumSize : function(maxSize){
37652         this.maxSize = maxSize;
37653     },
37654     
37655     /**
37656      * Sets the initialize size for the resizing element
37657      * @param {Number} size The initial size
37658      */
37659     setCurrentSize : function(size){
37660         var oldAnimate = this.animate;
37661         this.animate = false;
37662         this.adapter.setElementSize(this, size);
37663         this.animate = oldAnimate;
37664     },
37665     
37666     /**
37667      * Destroy this splitbar. 
37668      * @param {Boolean} removeEl True to remove the element
37669      */
37670     destroy : function(removeEl){
37671         if(this.shim){
37672             this.shim.remove();
37673         }
37674         this.dd.unreg();
37675         this.proxy.parentNode.removeChild(this.proxy);
37676         if(removeEl){
37677             this.el.remove();
37678         }
37679     }
37680 });
37681
37682 /**
37683  * @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.
37684  */
37685 Roo.bootstrap.SplitBar.createProxy = function(dir){
37686     var proxy = new Roo.Element(document.createElement("div"));
37687     proxy.unselectable();
37688     var cls = 'roo-splitbar-proxy';
37689     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37690     document.body.appendChild(proxy.dom);
37691     return proxy.dom;
37692 };
37693
37694 /** 
37695  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37696  * Default Adapter. It assumes the splitter and resizing element are not positioned
37697  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37698  */
37699 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37700 };
37701
37702 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37703     // do nothing for now
37704     init : function(s){
37705     
37706     },
37707     /**
37708      * Called before drag operations to get the current size of the resizing element. 
37709      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37710      */
37711      getElementSize : function(s){
37712         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37713             return s.resizingEl.getWidth();
37714         }else{
37715             return s.resizingEl.getHeight();
37716         }
37717     },
37718     
37719     /**
37720      * Called after drag operations to set the size of the resizing element.
37721      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37722      * @param {Number} newSize The new size to set
37723      * @param {Function} onComplete A function to be invoked when resizing is complete
37724      */
37725     setElementSize : function(s, newSize, onComplete){
37726         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37727             if(!s.animate){
37728                 s.resizingEl.setWidth(newSize);
37729                 if(onComplete){
37730                     onComplete(s, newSize);
37731                 }
37732             }else{
37733                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37734             }
37735         }else{
37736             
37737             if(!s.animate){
37738                 s.resizingEl.setHeight(newSize);
37739                 if(onComplete){
37740                     onComplete(s, newSize);
37741                 }
37742             }else{
37743                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37744             }
37745         }
37746     }
37747 };
37748
37749 /** 
37750  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37751  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37752  * Adapter that  moves the splitter element to align with the resized sizing element. 
37753  * Used with an absolute positioned SplitBar.
37754  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37755  * document.body, make sure you assign an id to the body element.
37756  */
37757 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37758     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37759     this.container = Roo.get(container);
37760 };
37761
37762 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37763     init : function(s){
37764         this.basic.init(s);
37765     },
37766     
37767     getElementSize : function(s){
37768         return this.basic.getElementSize(s);
37769     },
37770     
37771     setElementSize : function(s, newSize, onComplete){
37772         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37773     },
37774     
37775     moveSplitter : function(s){
37776         var yes = Roo.bootstrap.SplitBar;
37777         switch(s.placement){
37778             case yes.LEFT:
37779                 s.el.setX(s.resizingEl.getRight());
37780                 break;
37781             case yes.RIGHT:
37782                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37783                 break;
37784             case yes.TOP:
37785                 s.el.setY(s.resizingEl.getBottom());
37786                 break;
37787             case yes.BOTTOM:
37788                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37789                 break;
37790         }
37791     }
37792 };
37793
37794 /**
37795  * Orientation constant - Create a vertical SplitBar
37796  * @static
37797  * @type Number
37798  */
37799 Roo.bootstrap.SplitBar.VERTICAL = 1;
37800
37801 /**
37802  * Orientation constant - Create a horizontal SplitBar
37803  * @static
37804  * @type Number
37805  */
37806 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37807
37808 /**
37809  * Placement constant - The resizing element is to the left of the splitter element
37810  * @static
37811  * @type Number
37812  */
37813 Roo.bootstrap.SplitBar.LEFT = 1;
37814
37815 /**
37816  * Placement constant - The resizing element is to the right of the splitter element
37817  * @static
37818  * @type Number
37819  */
37820 Roo.bootstrap.SplitBar.RIGHT = 2;
37821
37822 /**
37823  * Placement constant - The resizing element is positioned above the splitter element
37824  * @static
37825  * @type Number
37826  */
37827 Roo.bootstrap.SplitBar.TOP = 3;
37828
37829 /**
37830  * Placement constant - The resizing element is positioned under splitter element
37831  * @static
37832  * @type Number
37833  */
37834 Roo.bootstrap.SplitBar.BOTTOM = 4;
37835 /*
37836  * Based on:
37837  * Ext JS Library 1.1.1
37838  * Copyright(c) 2006-2007, Ext JS, LLC.
37839  *
37840  * Originally Released Under LGPL - original licence link has changed is not relivant.
37841  *
37842  * Fork - LGPL
37843  * <script type="text/javascript">
37844  */
37845
37846 /**
37847  * @class Roo.bootstrap.layout.Manager
37848  * @extends Roo.bootstrap.Component
37849  * @abstract
37850  * Base class for layout managers.
37851  */
37852 Roo.bootstrap.layout.Manager = function(config)
37853 {
37854     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37855
37856
37857
37858
37859
37860     /** false to disable window resize monitoring @type Boolean */
37861     this.monitorWindowResize = true;
37862     this.regions = {};
37863     this.addEvents({
37864         /**
37865          * @event layout
37866          * Fires when a layout is performed.
37867          * @param {Roo.LayoutManager} this
37868          */
37869         "layout" : true,
37870         /**
37871          * @event regionresized
37872          * Fires when the user resizes a region.
37873          * @param {Roo.LayoutRegion} region The resized region
37874          * @param {Number} newSize The new size (width for east/west, height for north/south)
37875          */
37876         "regionresized" : true,
37877         /**
37878          * @event regioncollapsed
37879          * Fires when a region is collapsed.
37880          * @param {Roo.LayoutRegion} region The collapsed region
37881          */
37882         "regioncollapsed" : true,
37883         /**
37884          * @event regionexpanded
37885          * Fires when a region is expanded.
37886          * @param {Roo.LayoutRegion} region The expanded region
37887          */
37888         "regionexpanded" : true
37889     });
37890     this.updating = false;
37891
37892     if (config.el) {
37893         this.el = Roo.get(config.el);
37894         this.initEvents();
37895     }
37896
37897 };
37898
37899 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37900
37901
37902     regions : null,
37903
37904     monitorWindowResize : true,
37905
37906
37907     updating : false,
37908
37909
37910     onRender : function(ct, position)
37911     {
37912         if(!this.el){
37913             this.el = Roo.get(ct);
37914             this.initEvents();
37915         }
37916         //this.fireEvent('render',this);
37917     },
37918
37919
37920     initEvents: function()
37921     {
37922
37923
37924         // ie scrollbar fix
37925         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37926             document.body.scroll = "no";
37927         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37928             this.el.position('relative');
37929         }
37930         this.id = this.el.id;
37931         this.el.addClass("roo-layout-container");
37932         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37933         if(this.el.dom != document.body ) {
37934             this.el.on('resize', this.layout,this);
37935             this.el.on('show', this.layout,this);
37936         }
37937
37938     },
37939
37940     /**
37941      * Returns true if this layout is currently being updated
37942      * @return {Boolean}
37943      */
37944     isUpdating : function(){
37945         return this.updating;
37946     },
37947
37948     /**
37949      * Suspend the LayoutManager from doing auto-layouts while
37950      * making multiple add or remove calls
37951      */
37952     beginUpdate : function(){
37953         this.updating = true;
37954     },
37955
37956     /**
37957      * Restore auto-layouts and optionally disable the manager from performing a layout
37958      * @param {Boolean} noLayout true to disable a layout update
37959      */
37960     endUpdate : function(noLayout){
37961         this.updating = false;
37962         if(!noLayout){
37963             this.layout();
37964         }
37965     },
37966
37967     layout: function(){
37968         // abstract...
37969     },
37970
37971     onRegionResized : function(region, newSize){
37972         this.fireEvent("regionresized", region, newSize);
37973         this.layout();
37974     },
37975
37976     onRegionCollapsed : function(region){
37977         this.fireEvent("regioncollapsed", region);
37978     },
37979
37980     onRegionExpanded : function(region){
37981         this.fireEvent("regionexpanded", region);
37982     },
37983
37984     /**
37985      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37986      * performs box-model adjustments.
37987      * @return {Object} The size as an object {width: (the width), height: (the height)}
37988      */
37989     getViewSize : function()
37990     {
37991         var size;
37992         if(this.el.dom != document.body){
37993             size = this.el.getSize();
37994         }else{
37995             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37996         }
37997         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37998         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37999         return size;
38000     },
38001
38002     /**
38003      * Returns the Element this layout is bound to.
38004      * @return {Roo.Element}
38005      */
38006     getEl : function(){
38007         return this.el;
38008     },
38009
38010     /**
38011      * Returns the specified region.
38012      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38013      * @return {Roo.LayoutRegion}
38014      */
38015     getRegion : function(target){
38016         return this.regions[target.toLowerCase()];
38017     },
38018
38019     onWindowResize : function(){
38020         if(this.monitorWindowResize){
38021             this.layout();
38022         }
38023     }
38024 });
38025 /*
38026  * Based on:
38027  * Ext JS Library 1.1.1
38028  * Copyright(c) 2006-2007, Ext JS, LLC.
38029  *
38030  * Originally Released Under LGPL - original licence link has changed is not relivant.
38031  *
38032  * Fork - LGPL
38033  * <script type="text/javascript">
38034  */
38035 /**
38036  * @class Roo.bootstrap.layout.Border
38037  * @extends Roo.bootstrap.layout.Manager
38038  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38039  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
38040  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38041  * please see: examples/bootstrap/nested.html<br><br>
38042  
38043 <b>The container the layout is rendered into can be either the body element or any other element.
38044 If it is not the body element, the container needs to either be an absolute positioned element,
38045 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38046 the container size if it is not the body element.</b>
38047
38048 * @constructor
38049 * Create a new Border
38050 * @param {Object} config Configuration options
38051  */
38052 Roo.bootstrap.layout.Border = function(config){
38053     config = config || {};
38054     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38055     
38056     
38057     
38058     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38059         if(config[region]){
38060             config[region].region = region;
38061             this.addRegion(config[region]);
38062         }
38063     },this);
38064     
38065 };
38066
38067 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38068
38069 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38070     
38071         /**
38072          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38073          */
38074         /**
38075          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38076          */
38077         /**
38078          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38079          */
38080         /**
38081          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38082          */
38083         /**
38084          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38085          */
38086         
38087         
38088         
38089         
38090     parent : false, // this might point to a 'nest' or a ???
38091     
38092     /**
38093      * Creates and adds a new region if it doesn't already exist.
38094      * @param {String} target The target region key (north, south, east, west or center).
38095      * @param {Object} config The regions config object
38096      * @return {BorderLayoutRegion} The new region
38097      */
38098     addRegion : function(config)
38099     {
38100         if(!this.regions[config.region]){
38101             var r = this.factory(config);
38102             this.bindRegion(r);
38103         }
38104         return this.regions[config.region];
38105     },
38106
38107     // private (kinda)
38108     bindRegion : function(r){
38109         this.regions[r.config.region] = r;
38110         
38111         r.on("visibilitychange",    this.layout, this);
38112         r.on("paneladded",          this.layout, this);
38113         r.on("panelremoved",        this.layout, this);
38114         r.on("invalidated",         this.layout, this);
38115         r.on("resized",             this.onRegionResized, this);
38116         r.on("collapsed",           this.onRegionCollapsed, this);
38117         r.on("expanded",            this.onRegionExpanded, this);
38118     },
38119
38120     /**
38121      * Performs a layout update.
38122      */
38123     layout : function()
38124     {
38125         if(this.updating) {
38126             return;
38127         }
38128         
38129         // render all the rebions if they have not been done alreayd?
38130         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38131             if(this.regions[region] && !this.regions[region].bodyEl){
38132                 this.regions[region].onRender(this.el)
38133             }
38134         },this);
38135         
38136         var size = this.getViewSize();
38137         var w = size.width;
38138         var h = size.height;
38139         var centerW = w;
38140         var centerH = h;
38141         var centerY = 0;
38142         var centerX = 0;
38143         //var x = 0, y = 0;
38144
38145         var rs = this.regions;
38146         var north = rs["north"];
38147         var south = rs["south"]; 
38148         var west = rs["west"];
38149         var east = rs["east"];
38150         var center = rs["center"];
38151         //if(this.hideOnLayout){ // not supported anymore
38152             //c.el.setStyle("display", "none");
38153         //}
38154         if(north && north.isVisible()){
38155             var b = north.getBox();
38156             var m = north.getMargins();
38157             b.width = w - (m.left+m.right);
38158             b.x = m.left;
38159             b.y = m.top;
38160             centerY = b.height + b.y + m.bottom;
38161             centerH -= centerY;
38162             north.updateBox(this.safeBox(b));
38163         }
38164         if(south && south.isVisible()){
38165             var b = south.getBox();
38166             var m = south.getMargins();
38167             b.width = w - (m.left+m.right);
38168             b.x = m.left;
38169             var totalHeight = (b.height + m.top + m.bottom);
38170             b.y = h - totalHeight + m.top;
38171             centerH -= totalHeight;
38172             south.updateBox(this.safeBox(b));
38173         }
38174         if(west && west.isVisible()){
38175             var b = west.getBox();
38176             var m = west.getMargins();
38177             b.height = centerH - (m.top+m.bottom);
38178             b.x = m.left;
38179             b.y = centerY + m.top;
38180             var totalWidth = (b.width + m.left + m.right);
38181             centerX += totalWidth;
38182             centerW -= totalWidth;
38183             west.updateBox(this.safeBox(b));
38184         }
38185         if(east && east.isVisible()){
38186             var b = east.getBox();
38187             var m = east.getMargins();
38188             b.height = centerH - (m.top+m.bottom);
38189             var totalWidth = (b.width + m.left + m.right);
38190             b.x = w - totalWidth + m.left;
38191             b.y = centerY + m.top;
38192             centerW -= totalWidth;
38193             east.updateBox(this.safeBox(b));
38194         }
38195         if(center){
38196             var m = center.getMargins();
38197             var centerBox = {
38198                 x: centerX + m.left,
38199                 y: centerY + m.top,
38200                 width: centerW - (m.left+m.right),
38201                 height: centerH - (m.top+m.bottom)
38202             };
38203             //if(this.hideOnLayout){
38204                 //center.el.setStyle("display", "block");
38205             //}
38206             center.updateBox(this.safeBox(centerBox));
38207         }
38208         this.el.repaint();
38209         this.fireEvent("layout", this);
38210     },
38211
38212     // private
38213     safeBox : function(box){
38214         box.width = Math.max(0, box.width);
38215         box.height = Math.max(0, box.height);
38216         return box;
38217     },
38218
38219     /**
38220      * Adds a ContentPanel (or subclass) to this layout.
38221      * @param {String} target The target region key (north, south, east, west or center).
38222      * @param {Roo.ContentPanel} panel The panel to add
38223      * @return {Roo.ContentPanel} The added panel
38224      */
38225     add : function(target, panel){
38226          
38227         target = target.toLowerCase();
38228         return this.regions[target].add(panel);
38229     },
38230
38231     /**
38232      * Remove a ContentPanel (or subclass) to this layout.
38233      * @param {String} target The target region key (north, south, east, west or center).
38234      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38235      * @return {Roo.ContentPanel} The removed panel
38236      */
38237     remove : function(target, panel){
38238         target = target.toLowerCase();
38239         return this.regions[target].remove(panel);
38240     },
38241
38242     /**
38243      * Searches all regions for a panel with the specified id
38244      * @param {String} panelId
38245      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38246      */
38247     findPanel : function(panelId){
38248         var rs = this.regions;
38249         for(var target in rs){
38250             if(typeof rs[target] != "function"){
38251                 var p = rs[target].getPanel(panelId);
38252                 if(p){
38253                     return p;
38254                 }
38255             }
38256         }
38257         return null;
38258     },
38259
38260     /**
38261      * Searches all regions for a panel with the specified id and activates (shows) it.
38262      * @param {String/ContentPanel} panelId The panels id or the panel itself
38263      * @return {Roo.ContentPanel} The shown panel or null
38264      */
38265     showPanel : function(panelId) {
38266       var rs = this.regions;
38267       for(var target in rs){
38268          var r = rs[target];
38269          if(typeof r != "function"){
38270             if(r.hasPanel(panelId)){
38271                return r.showPanel(panelId);
38272             }
38273          }
38274       }
38275       return null;
38276    },
38277
38278    /**
38279      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38280      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38281      */
38282    /*
38283     restoreState : function(provider){
38284         if(!provider){
38285             provider = Roo.state.Manager;
38286         }
38287         var sm = new Roo.LayoutStateManager();
38288         sm.init(this, provider);
38289     },
38290 */
38291  
38292  
38293     /**
38294      * Adds a xtype elements to the layout.
38295      * <pre><code>
38296
38297 layout.addxtype({
38298        xtype : 'ContentPanel',
38299        region: 'west',
38300        items: [ .... ]
38301    }
38302 );
38303
38304 layout.addxtype({
38305         xtype : 'NestedLayoutPanel',
38306         region: 'west',
38307         layout: {
38308            center: { },
38309            west: { }   
38310         },
38311         items : [ ... list of content panels or nested layout panels.. ]
38312    }
38313 );
38314 </code></pre>
38315      * @param {Object} cfg Xtype definition of item to add.
38316      */
38317     addxtype : function(cfg)
38318     {
38319         // basically accepts a pannel...
38320         // can accept a layout region..!?!?
38321         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38322         
38323         
38324         // theory?  children can only be panels??
38325         
38326         //if (!cfg.xtype.match(/Panel$/)) {
38327         //    return false;
38328         //}
38329         var ret = false;
38330         
38331         if (typeof(cfg.region) == 'undefined') {
38332             Roo.log("Failed to add Panel, region was not set");
38333             Roo.log(cfg);
38334             return false;
38335         }
38336         var region = cfg.region;
38337         delete cfg.region;
38338         
38339           
38340         var xitems = [];
38341         if (cfg.items) {
38342             xitems = cfg.items;
38343             delete cfg.items;
38344         }
38345         var nb = false;
38346         
38347         if ( region == 'center') {
38348             Roo.log("Center: " + cfg.title);
38349         }
38350         
38351         
38352         switch(cfg.xtype) 
38353         {
38354             case 'Content':  // ContentPanel (el, cfg)
38355             case 'Scroll':  // ContentPanel (el, cfg)
38356             case 'View': 
38357                 cfg.autoCreate = cfg.autoCreate || true;
38358                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38359                 //} else {
38360                 //    var el = this.el.createChild();
38361                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38362                 //}
38363                 
38364                 this.add(region, ret);
38365                 break;
38366             
38367             /*
38368             case 'TreePanel': // our new panel!
38369                 cfg.el = this.el.createChild();
38370                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38371                 this.add(region, ret);
38372                 break;
38373             */
38374             
38375             case 'Nest': 
38376                 // create a new Layout (which is  a Border Layout...
38377                 
38378                 var clayout = cfg.layout;
38379                 clayout.el  = this.el.createChild();
38380                 clayout.items   = clayout.items  || [];
38381                 
38382                 delete cfg.layout;
38383                 
38384                 // replace this exitems with the clayout ones..
38385                 xitems = clayout.items;
38386                  
38387                 // force background off if it's in center...
38388                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38389                     cfg.background = false;
38390                 }
38391                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38392                 
38393                 
38394                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38395                 //console.log('adding nested layout panel '  + cfg.toSource());
38396                 this.add(region, ret);
38397                 nb = {}; /// find first...
38398                 break;
38399             
38400             case 'Grid':
38401                 
38402                 // needs grid and region
38403                 
38404                 //var el = this.getRegion(region).el.createChild();
38405                 /*
38406                  *var el = this.el.createChild();
38407                 // create the grid first...
38408                 cfg.grid.container = el;
38409                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38410                 */
38411                 
38412                 if (region == 'center' && this.active ) {
38413                     cfg.background = false;
38414                 }
38415                 
38416                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38417                 
38418                 this.add(region, ret);
38419                 /*
38420                 if (cfg.background) {
38421                     // render grid on panel activation (if panel background)
38422                     ret.on('activate', function(gp) {
38423                         if (!gp.grid.rendered) {
38424                     //        gp.grid.render(el);
38425                         }
38426                     });
38427                 } else {
38428                   //  cfg.grid.render(el);
38429                 }
38430                 */
38431                 break;
38432            
38433            
38434             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38435                 // it was the old xcomponent building that caused this before.
38436                 // espeically if border is the top element in the tree.
38437                 ret = this;
38438                 break; 
38439                 
38440                     
38441                 
38442                 
38443                 
38444             default:
38445                 /*
38446                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38447                     
38448                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38449                     this.add(region, ret);
38450                 } else {
38451                 */
38452                     Roo.log(cfg);
38453                     throw "Can not add '" + cfg.xtype + "' to Border";
38454                     return null;
38455              
38456                                 
38457              
38458         }
38459         this.beginUpdate();
38460         // add children..
38461         var region = '';
38462         var abn = {};
38463         Roo.each(xitems, function(i)  {
38464             region = nb && i.region ? i.region : false;
38465             
38466             var add = ret.addxtype(i);
38467            
38468             if (region) {
38469                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38470                 if (!i.background) {
38471                     abn[region] = nb[region] ;
38472                 }
38473             }
38474             
38475         });
38476         this.endUpdate();
38477
38478         // make the last non-background panel active..
38479         //if (nb) { Roo.log(abn); }
38480         if (nb) {
38481             
38482             for(var r in abn) {
38483                 region = this.getRegion(r);
38484                 if (region) {
38485                     // tried using nb[r], but it does not work..
38486                      
38487                     region.showPanel(abn[r]);
38488                    
38489                 }
38490             }
38491         }
38492         return ret;
38493         
38494     },
38495     
38496     
38497 // private
38498     factory : function(cfg)
38499     {
38500         
38501         var validRegions = Roo.bootstrap.layout.Border.regions;
38502
38503         var target = cfg.region;
38504         cfg.mgr = this;
38505         
38506         var r = Roo.bootstrap.layout;
38507         Roo.log(target);
38508         switch(target){
38509             case "north":
38510                 return new r.North(cfg);
38511             case "south":
38512                 return new r.South(cfg);
38513             case "east":
38514                 return new r.East(cfg);
38515             case "west":
38516                 return new r.West(cfg);
38517             case "center":
38518                 return new r.Center(cfg);
38519         }
38520         throw 'Layout region "'+target+'" not supported.';
38521     }
38522     
38523     
38524 });
38525  /*
38526  * Based on:
38527  * Ext JS Library 1.1.1
38528  * Copyright(c) 2006-2007, Ext JS, LLC.
38529  *
38530  * Originally Released Under LGPL - original licence link has changed is not relivant.
38531  *
38532  * Fork - LGPL
38533  * <script type="text/javascript">
38534  */
38535  
38536 /**
38537  * @class Roo.bootstrap.layout.Basic
38538  * @extends Roo.util.Observable
38539  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38540  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38541  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38542  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38543  * @cfg {string}   region  the region that it inhabits..
38544  * @cfg {bool}   skipConfig skip config?
38545  * 
38546
38547  */
38548 Roo.bootstrap.layout.Basic = function(config){
38549     
38550     this.mgr = config.mgr;
38551     
38552     this.position = config.region;
38553     
38554     var skipConfig = config.skipConfig;
38555     
38556     this.events = {
38557         /**
38558          * @scope Roo.BasicLayoutRegion
38559          */
38560         
38561         /**
38562          * @event beforeremove
38563          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38564          * @param {Roo.LayoutRegion} this
38565          * @param {Roo.ContentPanel} panel The panel
38566          * @param {Object} e The cancel event object
38567          */
38568         "beforeremove" : true,
38569         /**
38570          * @event invalidated
38571          * Fires when the layout for this region is changed.
38572          * @param {Roo.LayoutRegion} this
38573          */
38574         "invalidated" : true,
38575         /**
38576          * @event visibilitychange
38577          * Fires when this region is shown or hidden 
38578          * @param {Roo.LayoutRegion} this
38579          * @param {Boolean} visibility true or false
38580          */
38581         "visibilitychange" : true,
38582         /**
38583          * @event paneladded
38584          * Fires when a panel is added. 
38585          * @param {Roo.LayoutRegion} this
38586          * @param {Roo.ContentPanel} panel The panel
38587          */
38588         "paneladded" : true,
38589         /**
38590          * @event panelremoved
38591          * Fires when a panel is removed. 
38592          * @param {Roo.LayoutRegion} this
38593          * @param {Roo.ContentPanel} panel The panel
38594          */
38595         "panelremoved" : true,
38596         /**
38597          * @event beforecollapse
38598          * Fires when this region before collapse.
38599          * @param {Roo.LayoutRegion} this
38600          */
38601         "beforecollapse" : true,
38602         /**
38603          * @event collapsed
38604          * Fires when this region is collapsed.
38605          * @param {Roo.LayoutRegion} this
38606          */
38607         "collapsed" : true,
38608         /**
38609          * @event expanded
38610          * Fires when this region is expanded.
38611          * @param {Roo.LayoutRegion} this
38612          */
38613         "expanded" : true,
38614         /**
38615          * @event slideshow
38616          * Fires when this region is slid into view.
38617          * @param {Roo.LayoutRegion} this
38618          */
38619         "slideshow" : true,
38620         /**
38621          * @event slidehide
38622          * Fires when this region slides out of view. 
38623          * @param {Roo.LayoutRegion} this
38624          */
38625         "slidehide" : true,
38626         /**
38627          * @event panelactivated
38628          * Fires when a panel is activated. 
38629          * @param {Roo.LayoutRegion} this
38630          * @param {Roo.ContentPanel} panel The activated panel
38631          */
38632         "panelactivated" : true,
38633         /**
38634          * @event resized
38635          * Fires when the user resizes this region. 
38636          * @param {Roo.LayoutRegion} this
38637          * @param {Number} newSize The new size (width for east/west, height for north/south)
38638          */
38639         "resized" : true
38640     };
38641     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38642     this.panels = new Roo.util.MixedCollection();
38643     this.panels.getKey = this.getPanelId.createDelegate(this);
38644     this.box = null;
38645     this.activePanel = null;
38646     // ensure listeners are added...
38647     
38648     if (config.listeners || config.events) {
38649         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38650             listeners : config.listeners || {},
38651             events : config.events || {}
38652         });
38653     }
38654     
38655     if(skipConfig !== true){
38656         this.applyConfig(config);
38657     }
38658 };
38659
38660 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38661 {
38662     getPanelId : function(p){
38663         return p.getId();
38664     },
38665     
38666     applyConfig : function(config){
38667         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38668         this.config = config;
38669         
38670     },
38671     
38672     /**
38673      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38674      * the width, for horizontal (north, south) the height.
38675      * @param {Number} newSize The new width or height
38676      */
38677     resizeTo : function(newSize){
38678         var el = this.el ? this.el :
38679                  (this.activePanel ? this.activePanel.getEl() : null);
38680         if(el){
38681             switch(this.position){
38682                 case "east":
38683                 case "west":
38684                     el.setWidth(newSize);
38685                     this.fireEvent("resized", this, newSize);
38686                 break;
38687                 case "north":
38688                 case "south":
38689                     el.setHeight(newSize);
38690                     this.fireEvent("resized", this, newSize);
38691                 break;                
38692             }
38693         }
38694     },
38695     
38696     getBox : function(){
38697         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38698     },
38699     
38700     getMargins : function(){
38701         return this.margins;
38702     },
38703     
38704     updateBox : function(box){
38705         this.box = box;
38706         var el = this.activePanel.getEl();
38707         el.dom.style.left = box.x + "px";
38708         el.dom.style.top = box.y + "px";
38709         this.activePanel.setSize(box.width, box.height);
38710     },
38711     
38712     /**
38713      * Returns the container element for this region.
38714      * @return {Roo.Element}
38715      */
38716     getEl : function(){
38717         return this.activePanel;
38718     },
38719     
38720     /**
38721      * Returns true if this region is currently visible.
38722      * @return {Boolean}
38723      */
38724     isVisible : function(){
38725         return this.activePanel ? true : false;
38726     },
38727     
38728     setActivePanel : function(panel){
38729         panel = this.getPanel(panel);
38730         if(this.activePanel && this.activePanel != panel){
38731             this.activePanel.setActiveState(false);
38732             this.activePanel.getEl().setLeftTop(-10000,-10000);
38733         }
38734         this.activePanel = panel;
38735         panel.setActiveState(true);
38736         if(this.box){
38737             panel.setSize(this.box.width, this.box.height);
38738         }
38739         this.fireEvent("panelactivated", this, panel);
38740         this.fireEvent("invalidated");
38741     },
38742     
38743     /**
38744      * Show the specified panel.
38745      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38746      * @return {Roo.ContentPanel} The shown panel or null
38747      */
38748     showPanel : function(panel){
38749         panel = this.getPanel(panel);
38750         if(panel){
38751             this.setActivePanel(panel);
38752         }
38753         return panel;
38754     },
38755     
38756     /**
38757      * Get the active panel for this region.
38758      * @return {Roo.ContentPanel} The active panel or null
38759      */
38760     getActivePanel : function(){
38761         return this.activePanel;
38762     },
38763     
38764     /**
38765      * Add the passed ContentPanel(s)
38766      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38767      * @return {Roo.ContentPanel} The panel added (if only one was added)
38768      */
38769     add : function(panel){
38770         if(arguments.length > 1){
38771             for(var i = 0, len = arguments.length; i < len; i++) {
38772                 this.add(arguments[i]);
38773             }
38774             return null;
38775         }
38776         if(this.hasPanel(panel)){
38777             this.showPanel(panel);
38778             return panel;
38779         }
38780         var el = panel.getEl();
38781         if(el.dom.parentNode != this.mgr.el.dom){
38782             this.mgr.el.dom.appendChild(el.dom);
38783         }
38784         if(panel.setRegion){
38785             panel.setRegion(this);
38786         }
38787         this.panels.add(panel);
38788         el.setStyle("position", "absolute");
38789         if(!panel.background){
38790             this.setActivePanel(panel);
38791             if(this.config.initialSize && this.panels.getCount()==1){
38792                 this.resizeTo(this.config.initialSize);
38793             }
38794         }
38795         this.fireEvent("paneladded", this, panel);
38796         return panel;
38797     },
38798     
38799     /**
38800      * Returns true if the panel is in this region.
38801      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38802      * @return {Boolean}
38803      */
38804     hasPanel : function(panel){
38805         if(typeof panel == "object"){ // must be panel obj
38806             panel = panel.getId();
38807         }
38808         return this.getPanel(panel) ? true : false;
38809     },
38810     
38811     /**
38812      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38813      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38814      * @param {Boolean} preservePanel Overrides the config preservePanel option
38815      * @return {Roo.ContentPanel} The panel that was removed
38816      */
38817     remove : function(panel, preservePanel){
38818         panel = this.getPanel(panel);
38819         if(!panel){
38820             return null;
38821         }
38822         var e = {};
38823         this.fireEvent("beforeremove", this, panel, e);
38824         if(e.cancel === true){
38825             return null;
38826         }
38827         var panelId = panel.getId();
38828         this.panels.removeKey(panelId);
38829         return panel;
38830     },
38831     
38832     /**
38833      * Returns the panel specified or null if it's not in this region.
38834      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38835      * @return {Roo.ContentPanel}
38836      */
38837     getPanel : function(id){
38838         if(typeof id == "object"){ // must be panel obj
38839             return id;
38840         }
38841         return this.panels.get(id);
38842     },
38843     
38844     /**
38845      * Returns this regions position (north/south/east/west/center).
38846      * @return {String} 
38847      */
38848     getPosition: function(){
38849         return this.position;    
38850     }
38851 });/*
38852  * Based on:
38853  * Ext JS Library 1.1.1
38854  * Copyright(c) 2006-2007, Ext JS, LLC.
38855  *
38856  * Originally Released Under LGPL - original licence link has changed is not relivant.
38857  *
38858  * Fork - LGPL
38859  * <script type="text/javascript">
38860  */
38861  
38862 /**
38863  * @class Roo.bootstrap.layout.Region
38864  * @extends Roo.bootstrap.layout.Basic
38865  * This class represents a region in a layout manager.
38866  
38867  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38868  * @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})
38869  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38870  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38871  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38872  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38873  * @cfg {String}    title           The title for the region (overrides panel titles)
38874  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38875  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38876  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38877  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38878  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38879  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38880  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38881  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38882  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38883  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38884
38885  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38886  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38887  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38888  * @cfg {Number}    width           For East/West panels
38889  * @cfg {Number}    height          For North/South panels
38890  * @cfg {Boolean}   split           To show the splitter
38891  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38892  * 
38893  * @cfg {string}   cls             Extra CSS classes to add to region
38894  * 
38895  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38896  * @cfg {string}   region  the region that it inhabits..
38897  *
38898
38899  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38900  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38901
38902  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38903  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38904  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38905  */
38906 Roo.bootstrap.layout.Region = function(config)
38907 {
38908     this.applyConfig(config);
38909
38910     var mgr = config.mgr;
38911     var pos = config.region;
38912     config.skipConfig = true;
38913     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38914     
38915     if (mgr.el) {
38916         this.onRender(mgr.el);   
38917     }
38918      
38919     this.visible = true;
38920     this.collapsed = false;
38921     this.unrendered_panels = [];
38922 };
38923
38924 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38925
38926     position: '', // set by wrapper (eg. north/south etc..)
38927     unrendered_panels : null,  // unrendered panels.
38928     
38929     tabPosition : false,
38930     
38931     mgr: false, // points to 'Border'
38932     
38933     
38934     createBody : function(){
38935         /** This region's body element 
38936         * @type Roo.Element */
38937         this.bodyEl = this.el.createChild({
38938                 tag: "div",
38939                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38940         });
38941     },
38942
38943     onRender: function(ctr, pos)
38944     {
38945         var dh = Roo.DomHelper;
38946         /** This region's container element 
38947         * @type Roo.Element */
38948         this.el = dh.append(ctr.dom, {
38949                 tag: "div",
38950                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38951             }, true);
38952         /** This region's title element 
38953         * @type Roo.Element */
38954     
38955         this.titleEl = dh.append(this.el.dom,  {
38956                 tag: "div",
38957                 unselectable: "on",
38958                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38959                 children:[
38960                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38961                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38962                 ]
38963             }, true);
38964         
38965         this.titleEl.enableDisplayMode();
38966         /** This region's title text element 
38967         * @type HTMLElement */
38968         this.titleTextEl = this.titleEl.dom.firstChild;
38969         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38970         /*
38971         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38972         this.closeBtn.enableDisplayMode();
38973         this.closeBtn.on("click", this.closeClicked, this);
38974         this.closeBtn.hide();
38975     */
38976         this.createBody(this.config);
38977         if(this.config.hideWhenEmpty){
38978             this.hide();
38979             this.on("paneladded", this.validateVisibility, this);
38980             this.on("panelremoved", this.validateVisibility, this);
38981         }
38982         if(this.autoScroll){
38983             this.bodyEl.setStyle("overflow", "auto");
38984         }else{
38985             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38986         }
38987         //if(c.titlebar !== false){
38988             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38989                 this.titleEl.hide();
38990             }else{
38991                 this.titleEl.show();
38992                 if(this.config.title){
38993                     this.titleTextEl.innerHTML = this.config.title;
38994                 }
38995             }
38996         //}
38997         if(this.config.collapsed){
38998             this.collapse(true);
38999         }
39000         if(this.config.hidden){
39001             this.hide();
39002         }
39003         
39004         if (this.unrendered_panels && this.unrendered_panels.length) {
39005             for (var i =0;i< this.unrendered_panels.length; i++) {
39006                 this.add(this.unrendered_panels[i]);
39007             }
39008             this.unrendered_panels = null;
39009             
39010         }
39011         
39012     },
39013     
39014     applyConfig : function(c)
39015     {
39016         /*
39017          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39018             var dh = Roo.DomHelper;
39019             if(c.titlebar !== false){
39020                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39021                 this.collapseBtn.on("click", this.collapse, this);
39022                 this.collapseBtn.enableDisplayMode();
39023                 /*
39024                 if(c.showPin === true || this.showPin){
39025                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39026                     this.stickBtn.enableDisplayMode();
39027                     this.stickBtn.on("click", this.expand, this);
39028                     this.stickBtn.hide();
39029                 }
39030                 
39031             }
39032             */
39033             /** This region's collapsed element
39034             * @type Roo.Element */
39035             /*
39036              *
39037             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39038                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39039             ]}, true);
39040             
39041             if(c.floatable !== false){
39042                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39043                this.collapsedEl.on("click", this.collapseClick, this);
39044             }
39045
39046             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39047                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39048                    id: "message", unselectable: "on", style:{"float":"left"}});
39049                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39050              }
39051             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39052             this.expandBtn.on("click", this.expand, this);
39053             
39054         }
39055         
39056         if(this.collapseBtn){
39057             this.collapseBtn.setVisible(c.collapsible == true);
39058         }
39059         
39060         this.cmargins = c.cmargins || this.cmargins ||
39061                          (this.position == "west" || this.position == "east" ?
39062                              {top: 0, left: 2, right:2, bottom: 0} :
39063                              {top: 2, left: 0, right:0, bottom: 2});
39064         */
39065         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39066         
39067         
39068         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39069         
39070         this.autoScroll = c.autoScroll || false;
39071         
39072         
39073        
39074         
39075         this.duration = c.duration || .30;
39076         this.slideDuration = c.slideDuration || .45;
39077         this.config = c;
39078        
39079     },
39080     /**
39081      * Returns true if this region is currently visible.
39082      * @return {Boolean}
39083      */
39084     isVisible : function(){
39085         return this.visible;
39086     },
39087
39088     /**
39089      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39090      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39091      */
39092     //setCollapsedTitle : function(title){
39093     //    title = title || "&#160;";
39094      //   if(this.collapsedTitleTextEl){
39095       //      this.collapsedTitleTextEl.innerHTML = title;
39096        // }
39097     //},
39098
39099     getBox : function(){
39100         var b;
39101       //  if(!this.collapsed){
39102             b = this.el.getBox(false, true);
39103        // }else{
39104           //  b = this.collapsedEl.getBox(false, true);
39105         //}
39106         return b;
39107     },
39108
39109     getMargins : function(){
39110         return this.margins;
39111         //return this.collapsed ? this.cmargins : this.margins;
39112     },
39113 /*
39114     highlight : function(){
39115         this.el.addClass("x-layout-panel-dragover");
39116     },
39117
39118     unhighlight : function(){
39119         this.el.removeClass("x-layout-panel-dragover");
39120     },
39121 */
39122     updateBox : function(box)
39123     {
39124         if (!this.bodyEl) {
39125             return; // not rendered yet..
39126         }
39127         
39128         this.box = box;
39129         if(!this.collapsed){
39130             this.el.dom.style.left = box.x + "px";
39131             this.el.dom.style.top = box.y + "px";
39132             this.updateBody(box.width, box.height);
39133         }else{
39134             this.collapsedEl.dom.style.left = box.x + "px";
39135             this.collapsedEl.dom.style.top = box.y + "px";
39136             this.collapsedEl.setSize(box.width, box.height);
39137         }
39138         if(this.tabs){
39139             this.tabs.autoSizeTabs();
39140         }
39141     },
39142
39143     updateBody : function(w, h)
39144     {
39145         if(w !== null){
39146             this.el.setWidth(w);
39147             w -= this.el.getBorderWidth("rl");
39148             if(this.config.adjustments){
39149                 w += this.config.adjustments[0];
39150             }
39151         }
39152         if(h !== null && h > 0){
39153             this.el.setHeight(h);
39154             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39155             h -= this.el.getBorderWidth("tb");
39156             if(this.config.adjustments){
39157                 h += this.config.adjustments[1];
39158             }
39159             this.bodyEl.setHeight(h);
39160             if(this.tabs){
39161                 h = this.tabs.syncHeight(h);
39162             }
39163         }
39164         if(this.panelSize){
39165             w = w !== null ? w : this.panelSize.width;
39166             h = h !== null ? h : this.panelSize.height;
39167         }
39168         if(this.activePanel){
39169             var el = this.activePanel.getEl();
39170             w = w !== null ? w : el.getWidth();
39171             h = h !== null ? h : el.getHeight();
39172             this.panelSize = {width: w, height: h};
39173             this.activePanel.setSize(w, h);
39174         }
39175         if(Roo.isIE && this.tabs){
39176             this.tabs.el.repaint();
39177         }
39178     },
39179
39180     /**
39181      * Returns the container element for this region.
39182      * @return {Roo.Element}
39183      */
39184     getEl : function(){
39185         return this.el;
39186     },
39187
39188     /**
39189      * Hides this region.
39190      */
39191     hide : function(){
39192         //if(!this.collapsed){
39193             this.el.dom.style.left = "-2000px";
39194             this.el.hide();
39195         //}else{
39196          //   this.collapsedEl.dom.style.left = "-2000px";
39197          //   this.collapsedEl.hide();
39198        // }
39199         this.visible = false;
39200         this.fireEvent("visibilitychange", this, false);
39201     },
39202
39203     /**
39204      * Shows this region if it was previously hidden.
39205      */
39206     show : function(){
39207         //if(!this.collapsed){
39208             this.el.show();
39209         //}else{
39210         //    this.collapsedEl.show();
39211        // }
39212         this.visible = true;
39213         this.fireEvent("visibilitychange", this, true);
39214     },
39215 /*
39216     closeClicked : function(){
39217         if(this.activePanel){
39218             this.remove(this.activePanel);
39219         }
39220     },
39221
39222     collapseClick : function(e){
39223         if(this.isSlid){
39224            e.stopPropagation();
39225            this.slideIn();
39226         }else{
39227            e.stopPropagation();
39228            this.slideOut();
39229         }
39230     },
39231 */
39232     /**
39233      * Collapses this region.
39234      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39235      */
39236     /*
39237     collapse : function(skipAnim, skipCheck = false){
39238         if(this.collapsed) {
39239             return;
39240         }
39241         
39242         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39243             
39244             this.collapsed = true;
39245             if(this.split){
39246                 this.split.el.hide();
39247             }
39248             if(this.config.animate && skipAnim !== true){
39249                 this.fireEvent("invalidated", this);
39250                 this.animateCollapse();
39251             }else{
39252                 this.el.setLocation(-20000,-20000);
39253                 this.el.hide();
39254                 this.collapsedEl.show();
39255                 this.fireEvent("collapsed", this);
39256                 this.fireEvent("invalidated", this);
39257             }
39258         }
39259         
39260     },
39261 */
39262     animateCollapse : function(){
39263         // overridden
39264     },
39265
39266     /**
39267      * Expands this region if it was previously collapsed.
39268      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39269      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39270      */
39271     /*
39272     expand : function(e, skipAnim){
39273         if(e) {
39274             e.stopPropagation();
39275         }
39276         if(!this.collapsed || this.el.hasActiveFx()) {
39277             return;
39278         }
39279         if(this.isSlid){
39280             this.afterSlideIn();
39281             skipAnim = true;
39282         }
39283         this.collapsed = false;
39284         if(this.config.animate && skipAnim !== true){
39285             this.animateExpand();
39286         }else{
39287             this.el.show();
39288             if(this.split){
39289                 this.split.el.show();
39290             }
39291             this.collapsedEl.setLocation(-2000,-2000);
39292             this.collapsedEl.hide();
39293             this.fireEvent("invalidated", this);
39294             this.fireEvent("expanded", this);
39295         }
39296     },
39297 */
39298     animateExpand : function(){
39299         // overridden
39300     },
39301
39302     initTabs : function()
39303     {
39304         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39305         
39306         var ts = new Roo.bootstrap.panel.Tabs({
39307             el: this.bodyEl.dom,
39308             region : this,
39309             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39310             disableTooltips: this.config.disableTabTips,
39311             toolbar : this.config.toolbar
39312         });
39313         
39314         if(this.config.hideTabs){
39315             ts.stripWrap.setDisplayed(false);
39316         }
39317         this.tabs = ts;
39318         ts.resizeTabs = this.config.resizeTabs === true;
39319         ts.minTabWidth = this.config.minTabWidth || 40;
39320         ts.maxTabWidth = this.config.maxTabWidth || 250;
39321         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39322         ts.monitorResize = false;
39323         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39324         ts.bodyEl.addClass('roo-layout-tabs-body');
39325         this.panels.each(this.initPanelAsTab, this);
39326     },
39327
39328     initPanelAsTab : function(panel){
39329         var ti = this.tabs.addTab(
39330             panel.getEl().id,
39331             panel.getTitle(),
39332             null,
39333             this.config.closeOnTab && panel.isClosable(),
39334             panel.tpl
39335         );
39336         if(panel.tabTip !== undefined){
39337             ti.setTooltip(panel.tabTip);
39338         }
39339         ti.on("activate", function(){
39340               this.setActivePanel(panel);
39341         }, this);
39342         
39343         if(this.config.closeOnTab){
39344             ti.on("beforeclose", function(t, e){
39345                 e.cancel = true;
39346                 this.remove(panel);
39347             }, this);
39348         }
39349         
39350         panel.tabItem = ti;
39351         
39352         return ti;
39353     },
39354
39355     updatePanelTitle : function(panel, title)
39356     {
39357         if(this.activePanel == panel){
39358             this.updateTitle(title);
39359         }
39360         if(this.tabs){
39361             var ti = this.tabs.getTab(panel.getEl().id);
39362             ti.setText(title);
39363             if(panel.tabTip !== undefined){
39364                 ti.setTooltip(panel.tabTip);
39365             }
39366         }
39367     },
39368
39369     updateTitle : function(title){
39370         if(this.titleTextEl && !this.config.title){
39371             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39372         }
39373     },
39374
39375     setActivePanel : function(panel)
39376     {
39377         panel = this.getPanel(panel);
39378         if(this.activePanel && this.activePanel != panel){
39379             if(this.activePanel.setActiveState(false) === false){
39380                 return;
39381             }
39382         }
39383         this.activePanel = panel;
39384         panel.setActiveState(true);
39385         if(this.panelSize){
39386             panel.setSize(this.panelSize.width, this.panelSize.height);
39387         }
39388         if(this.closeBtn){
39389             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39390         }
39391         this.updateTitle(panel.getTitle());
39392         if(this.tabs){
39393             this.fireEvent("invalidated", this);
39394         }
39395         this.fireEvent("panelactivated", this, panel);
39396     },
39397
39398     /**
39399      * Shows the specified panel.
39400      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39401      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39402      */
39403     showPanel : function(panel)
39404     {
39405         panel = this.getPanel(panel);
39406         if(panel){
39407             if(this.tabs){
39408                 var tab = this.tabs.getTab(panel.getEl().id);
39409                 if(tab.isHidden()){
39410                     this.tabs.unhideTab(tab.id);
39411                 }
39412                 tab.activate();
39413             }else{
39414                 this.setActivePanel(panel);
39415             }
39416         }
39417         return panel;
39418     },
39419
39420     /**
39421      * Get the active panel for this region.
39422      * @return {Roo.ContentPanel} The active panel or null
39423      */
39424     getActivePanel : function(){
39425         return this.activePanel;
39426     },
39427
39428     validateVisibility : function(){
39429         if(this.panels.getCount() < 1){
39430             this.updateTitle("&#160;");
39431             this.closeBtn.hide();
39432             this.hide();
39433         }else{
39434             if(!this.isVisible()){
39435                 this.show();
39436             }
39437         }
39438     },
39439
39440     /**
39441      * Adds the passed ContentPanel(s) to this region.
39442      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39443      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39444      */
39445     add : function(panel)
39446     {
39447         if(arguments.length > 1){
39448             for(var i = 0, len = arguments.length; i < len; i++) {
39449                 this.add(arguments[i]);
39450             }
39451             return null;
39452         }
39453         
39454         // if we have not been rendered yet, then we can not really do much of this..
39455         if (!this.bodyEl) {
39456             this.unrendered_panels.push(panel);
39457             return panel;
39458         }
39459         
39460         
39461         
39462         
39463         if(this.hasPanel(panel)){
39464             this.showPanel(panel);
39465             return panel;
39466         }
39467         panel.setRegion(this);
39468         this.panels.add(panel);
39469        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39470             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39471             // and hide them... ???
39472             this.bodyEl.dom.appendChild(panel.getEl().dom);
39473             if(panel.background !== true){
39474                 this.setActivePanel(panel);
39475             }
39476             this.fireEvent("paneladded", this, panel);
39477             return panel;
39478         }
39479         */
39480         if(!this.tabs){
39481             this.initTabs();
39482         }else{
39483             this.initPanelAsTab(panel);
39484         }
39485         
39486         
39487         if(panel.background !== true){
39488             this.tabs.activate(panel.getEl().id);
39489         }
39490         this.fireEvent("paneladded", this, panel);
39491         return panel;
39492     },
39493
39494     /**
39495      * Hides the tab for the specified panel.
39496      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39497      */
39498     hidePanel : function(panel){
39499         if(this.tabs && (panel = this.getPanel(panel))){
39500             this.tabs.hideTab(panel.getEl().id);
39501         }
39502     },
39503
39504     /**
39505      * Unhides the tab for a previously hidden panel.
39506      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39507      */
39508     unhidePanel : function(panel){
39509         if(this.tabs && (panel = this.getPanel(panel))){
39510             this.tabs.unhideTab(panel.getEl().id);
39511         }
39512     },
39513
39514     clearPanels : function(){
39515         while(this.panels.getCount() > 0){
39516              this.remove(this.panels.first());
39517         }
39518     },
39519
39520     /**
39521      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39522      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39523      * @param {Boolean} preservePanel Overrides the config preservePanel option
39524      * @return {Roo.ContentPanel} The panel that was removed
39525      */
39526     remove : function(panel, preservePanel)
39527     {
39528         panel = this.getPanel(panel);
39529         if(!panel){
39530             return null;
39531         }
39532         var e = {};
39533         this.fireEvent("beforeremove", this, panel, e);
39534         if(e.cancel === true){
39535             return null;
39536         }
39537         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39538         var panelId = panel.getId();
39539         this.panels.removeKey(panelId);
39540         if(preservePanel){
39541             document.body.appendChild(panel.getEl().dom);
39542         }
39543         if(this.tabs){
39544             this.tabs.removeTab(panel.getEl().id);
39545         }else if (!preservePanel){
39546             this.bodyEl.dom.removeChild(panel.getEl().dom);
39547         }
39548         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39549             var p = this.panels.first();
39550             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39551             tempEl.appendChild(p.getEl().dom);
39552             this.bodyEl.update("");
39553             this.bodyEl.dom.appendChild(p.getEl().dom);
39554             tempEl = null;
39555             this.updateTitle(p.getTitle());
39556             this.tabs = null;
39557             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39558             this.setActivePanel(p);
39559         }
39560         panel.setRegion(null);
39561         if(this.activePanel == panel){
39562             this.activePanel = null;
39563         }
39564         if(this.config.autoDestroy !== false && preservePanel !== true){
39565             try{panel.destroy();}catch(e){}
39566         }
39567         this.fireEvent("panelremoved", this, panel);
39568         return panel;
39569     },
39570
39571     /**
39572      * Returns the TabPanel component used by this region
39573      * @return {Roo.TabPanel}
39574      */
39575     getTabs : function(){
39576         return this.tabs;
39577     },
39578
39579     createTool : function(parentEl, className){
39580         var btn = Roo.DomHelper.append(parentEl, {
39581             tag: "div",
39582             cls: "x-layout-tools-button",
39583             children: [ {
39584                 tag: "div",
39585                 cls: "roo-layout-tools-button-inner " + className,
39586                 html: "&#160;"
39587             }]
39588         }, true);
39589         btn.addClassOnOver("roo-layout-tools-button-over");
39590         return btn;
39591     }
39592 });/*
39593  * Based on:
39594  * Ext JS Library 1.1.1
39595  * Copyright(c) 2006-2007, Ext JS, LLC.
39596  *
39597  * Originally Released Under LGPL - original licence link has changed is not relivant.
39598  *
39599  * Fork - LGPL
39600  * <script type="text/javascript">
39601  */
39602  
39603
39604
39605 /**
39606  * @class Roo.SplitLayoutRegion
39607  * @extends Roo.LayoutRegion
39608  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39609  */
39610 Roo.bootstrap.layout.Split = function(config){
39611     this.cursor = config.cursor;
39612     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39613 };
39614
39615 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39616 {
39617     splitTip : "Drag to resize.",
39618     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39619     useSplitTips : false,
39620
39621     applyConfig : function(config){
39622         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39623     },
39624     
39625     onRender : function(ctr,pos) {
39626         
39627         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39628         if(!this.config.split){
39629             return;
39630         }
39631         if(!this.split){
39632             
39633             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39634                             tag: "div",
39635                             id: this.el.id + "-split",
39636                             cls: "roo-layout-split roo-layout-split-"+this.position,
39637                             html: "&#160;"
39638             });
39639             /** The SplitBar for this region 
39640             * @type Roo.SplitBar */
39641             // does not exist yet...
39642             Roo.log([this.position, this.orientation]);
39643             
39644             this.split = new Roo.bootstrap.SplitBar({
39645                 dragElement : splitEl,
39646                 resizingElement: this.el,
39647                 orientation : this.orientation
39648             });
39649             
39650             this.split.on("moved", this.onSplitMove, this);
39651             this.split.useShim = this.config.useShim === true;
39652             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39653             if(this.useSplitTips){
39654                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39655             }
39656             //if(config.collapsible){
39657             //    this.split.el.on("dblclick", this.collapse,  this);
39658             //}
39659         }
39660         if(typeof this.config.minSize != "undefined"){
39661             this.split.minSize = this.config.minSize;
39662         }
39663         if(typeof this.config.maxSize != "undefined"){
39664             this.split.maxSize = this.config.maxSize;
39665         }
39666         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39667             this.hideSplitter();
39668         }
39669         
39670     },
39671
39672     getHMaxSize : function(){
39673          var cmax = this.config.maxSize || 10000;
39674          var center = this.mgr.getRegion("center");
39675          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39676     },
39677
39678     getVMaxSize : function(){
39679          var cmax = this.config.maxSize || 10000;
39680          var center = this.mgr.getRegion("center");
39681          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39682     },
39683
39684     onSplitMove : function(split, newSize){
39685         this.fireEvent("resized", this, newSize);
39686     },
39687     
39688     /** 
39689      * Returns the {@link Roo.SplitBar} for this region.
39690      * @return {Roo.SplitBar}
39691      */
39692     getSplitBar : function(){
39693         return this.split;
39694     },
39695     
39696     hide : function(){
39697         this.hideSplitter();
39698         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39699     },
39700
39701     hideSplitter : function(){
39702         if(this.split){
39703             this.split.el.setLocation(-2000,-2000);
39704             this.split.el.hide();
39705         }
39706     },
39707
39708     show : function(){
39709         if(this.split){
39710             this.split.el.show();
39711         }
39712         Roo.bootstrap.layout.Split.superclass.show.call(this);
39713     },
39714     
39715     beforeSlide: function(){
39716         if(Roo.isGecko){// firefox overflow auto bug workaround
39717             this.bodyEl.clip();
39718             if(this.tabs) {
39719                 this.tabs.bodyEl.clip();
39720             }
39721             if(this.activePanel){
39722                 this.activePanel.getEl().clip();
39723                 
39724                 if(this.activePanel.beforeSlide){
39725                     this.activePanel.beforeSlide();
39726                 }
39727             }
39728         }
39729     },
39730     
39731     afterSlide : function(){
39732         if(Roo.isGecko){// firefox overflow auto bug workaround
39733             this.bodyEl.unclip();
39734             if(this.tabs) {
39735                 this.tabs.bodyEl.unclip();
39736             }
39737             if(this.activePanel){
39738                 this.activePanel.getEl().unclip();
39739                 if(this.activePanel.afterSlide){
39740                     this.activePanel.afterSlide();
39741                 }
39742             }
39743         }
39744     },
39745
39746     initAutoHide : function(){
39747         if(this.autoHide !== false){
39748             if(!this.autoHideHd){
39749                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39750                 this.autoHideHd = {
39751                     "mouseout": function(e){
39752                         if(!e.within(this.el, true)){
39753                             st.delay(500);
39754                         }
39755                     },
39756                     "mouseover" : function(e){
39757                         st.cancel();
39758                     },
39759                     scope : this
39760                 };
39761             }
39762             this.el.on(this.autoHideHd);
39763         }
39764     },
39765
39766     clearAutoHide : function(){
39767         if(this.autoHide !== false){
39768             this.el.un("mouseout", this.autoHideHd.mouseout);
39769             this.el.un("mouseover", this.autoHideHd.mouseover);
39770         }
39771     },
39772
39773     clearMonitor : function(){
39774         Roo.get(document).un("click", this.slideInIf, this);
39775     },
39776
39777     // these names are backwards but not changed for compat
39778     slideOut : function(){
39779         if(this.isSlid || this.el.hasActiveFx()){
39780             return;
39781         }
39782         this.isSlid = true;
39783         if(this.collapseBtn){
39784             this.collapseBtn.hide();
39785         }
39786         this.closeBtnState = this.closeBtn.getStyle('display');
39787         this.closeBtn.hide();
39788         if(this.stickBtn){
39789             this.stickBtn.show();
39790         }
39791         this.el.show();
39792         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39793         this.beforeSlide();
39794         this.el.setStyle("z-index", 10001);
39795         this.el.slideIn(this.getSlideAnchor(), {
39796             callback: function(){
39797                 this.afterSlide();
39798                 this.initAutoHide();
39799                 Roo.get(document).on("click", this.slideInIf, this);
39800                 this.fireEvent("slideshow", this);
39801             },
39802             scope: this,
39803             block: true
39804         });
39805     },
39806
39807     afterSlideIn : function(){
39808         this.clearAutoHide();
39809         this.isSlid = false;
39810         this.clearMonitor();
39811         this.el.setStyle("z-index", "");
39812         if(this.collapseBtn){
39813             this.collapseBtn.show();
39814         }
39815         this.closeBtn.setStyle('display', this.closeBtnState);
39816         if(this.stickBtn){
39817             this.stickBtn.hide();
39818         }
39819         this.fireEvent("slidehide", this);
39820     },
39821
39822     slideIn : function(cb){
39823         if(!this.isSlid || this.el.hasActiveFx()){
39824             Roo.callback(cb);
39825             return;
39826         }
39827         this.isSlid = false;
39828         this.beforeSlide();
39829         this.el.slideOut(this.getSlideAnchor(), {
39830             callback: function(){
39831                 this.el.setLeftTop(-10000, -10000);
39832                 this.afterSlide();
39833                 this.afterSlideIn();
39834                 Roo.callback(cb);
39835             },
39836             scope: this,
39837             block: true
39838         });
39839     },
39840     
39841     slideInIf : function(e){
39842         if(!e.within(this.el)){
39843             this.slideIn();
39844         }
39845     },
39846
39847     animateCollapse : function(){
39848         this.beforeSlide();
39849         this.el.setStyle("z-index", 20000);
39850         var anchor = this.getSlideAnchor();
39851         this.el.slideOut(anchor, {
39852             callback : function(){
39853                 this.el.setStyle("z-index", "");
39854                 this.collapsedEl.slideIn(anchor, {duration:.3});
39855                 this.afterSlide();
39856                 this.el.setLocation(-10000,-10000);
39857                 this.el.hide();
39858                 this.fireEvent("collapsed", this);
39859             },
39860             scope: this,
39861             block: true
39862         });
39863     },
39864
39865     animateExpand : function(){
39866         this.beforeSlide();
39867         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39868         this.el.setStyle("z-index", 20000);
39869         this.collapsedEl.hide({
39870             duration:.1
39871         });
39872         this.el.slideIn(this.getSlideAnchor(), {
39873             callback : function(){
39874                 this.el.setStyle("z-index", "");
39875                 this.afterSlide();
39876                 if(this.split){
39877                     this.split.el.show();
39878                 }
39879                 this.fireEvent("invalidated", this);
39880                 this.fireEvent("expanded", this);
39881             },
39882             scope: this,
39883             block: true
39884         });
39885     },
39886
39887     anchors : {
39888         "west" : "left",
39889         "east" : "right",
39890         "north" : "top",
39891         "south" : "bottom"
39892     },
39893
39894     sanchors : {
39895         "west" : "l",
39896         "east" : "r",
39897         "north" : "t",
39898         "south" : "b"
39899     },
39900
39901     canchors : {
39902         "west" : "tl-tr",
39903         "east" : "tr-tl",
39904         "north" : "tl-bl",
39905         "south" : "bl-tl"
39906     },
39907
39908     getAnchor : function(){
39909         return this.anchors[this.position];
39910     },
39911
39912     getCollapseAnchor : function(){
39913         return this.canchors[this.position];
39914     },
39915
39916     getSlideAnchor : function(){
39917         return this.sanchors[this.position];
39918     },
39919
39920     getAlignAdj : function(){
39921         var cm = this.cmargins;
39922         switch(this.position){
39923             case "west":
39924                 return [0, 0];
39925             break;
39926             case "east":
39927                 return [0, 0];
39928             break;
39929             case "north":
39930                 return [0, 0];
39931             break;
39932             case "south":
39933                 return [0, 0];
39934             break;
39935         }
39936     },
39937
39938     getExpandAdj : function(){
39939         var c = this.collapsedEl, cm = this.cmargins;
39940         switch(this.position){
39941             case "west":
39942                 return [-(cm.right+c.getWidth()+cm.left), 0];
39943             break;
39944             case "east":
39945                 return [cm.right+c.getWidth()+cm.left, 0];
39946             break;
39947             case "north":
39948                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39949             break;
39950             case "south":
39951                 return [0, cm.top+cm.bottom+c.getHeight()];
39952             break;
39953         }
39954     }
39955 });/*
39956  * Based on:
39957  * Ext JS Library 1.1.1
39958  * Copyright(c) 2006-2007, Ext JS, LLC.
39959  *
39960  * Originally Released Under LGPL - original licence link has changed is not relivant.
39961  *
39962  * Fork - LGPL
39963  * <script type="text/javascript">
39964  */
39965 /*
39966  * These classes are private internal classes
39967  */
39968 Roo.bootstrap.layout.Center = function(config){
39969     config.region = "center";
39970     Roo.bootstrap.layout.Region.call(this, config);
39971     this.visible = true;
39972     this.minWidth = config.minWidth || 20;
39973     this.minHeight = config.minHeight || 20;
39974 };
39975
39976 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39977     hide : function(){
39978         // center panel can't be hidden
39979     },
39980     
39981     show : function(){
39982         // center panel can't be hidden
39983     },
39984     
39985     getMinWidth: function(){
39986         return this.minWidth;
39987     },
39988     
39989     getMinHeight: function(){
39990         return this.minHeight;
39991     }
39992 });
39993
39994
39995
39996
39997  
39998
39999
40000
40001
40002
40003
40004 Roo.bootstrap.layout.North = function(config)
40005 {
40006     config.region = 'north';
40007     config.cursor = 'n-resize';
40008     
40009     Roo.bootstrap.layout.Split.call(this, config);
40010     
40011     
40012     if(this.split){
40013         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40014         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40015         this.split.el.addClass("roo-layout-split-v");
40016     }
40017     //var size = config.initialSize || config.height;
40018     //if(this.el && typeof size != "undefined"){
40019     //    this.el.setHeight(size);
40020     //}
40021 };
40022 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40023 {
40024     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40025      
40026      
40027     onRender : function(ctr, pos)
40028     {
40029         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40030         var size = this.config.initialSize || this.config.height;
40031         if(this.el && typeof size != "undefined"){
40032             this.el.setHeight(size);
40033         }
40034     
40035     },
40036     
40037     getBox : function(){
40038         if(this.collapsed){
40039             return this.collapsedEl.getBox();
40040         }
40041         var box = this.el.getBox();
40042         if(this.split){
40043             box.height += this.split.el.getHeight();
40044         }
40045         return box;
40046     },
40047     
40048     updateBox : function(box){
40049         if(this.split && !this.collapsed){
40050             box.height -= this.split.el.getHeight();
40051             this.split.el.setLeft(box.x);
40052             this.split.el.setTop(box.y+box.height);
40053             this.split.el.setWidth(box.width);
40054         }
40055         if(this.collapsed){
40056             this.updateBody(box.width, null);
40057         }
40058         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40059     }
40060 });
40061
40062
40063
40064
40065
40066 Roo.bootstrap.layout.South = function(config){
40067     config.region = 'south';
40068     config.cursor = 's-resize';
40069     Roo.bootstrap.layout.Split.call(this, config);
40070     if(this.split){
40071         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40072         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40073         this.split.el.addClass("roo-layout-split-v");
40074     }
40075     
40076 };
40077
40078 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40079     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40080     
40081     onRender : function(ctr, pos)
40082     {
40083         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40084         var size = this.config.initialSize || this.config.height;
40085         if(this.el && typeof size != "undefined"){
40086             this.el.setHeight(size);
40087         }
40088     
40089     },
40090     
40091     getBox : function(){
40092         if(this.collapsed){
40093             return this.collapsedEl.getBox();
40094         }
40095         var box = this.el.getBox();
40096         if(this.split){
40097             var sh = this.split.el.getHeight();
40098             box.height += sh;
40099             box.y -= sh;
40100         }
40101         return box;
40102     },
40103     
40104     updateBox : function(box){
40105         if(this.split && !this.collapsed){
40106             var sh = this.split.el.getHeight();
40107             box.height -= sh;
40108             box.y += sh;
40109             this.split.el.setLeft(box.x);
40110             this.split.el.setTop(box.y-sh);
40111             this.split.el.setWidth(box.width);
40112         }
40113         if(this.collapsed){
40114             this.updateBody(box.width, null);
40115         }
40116         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40117     }
40118 });
40119
40120 Roo.bootstrap.layout.East = function(config){
40121     config.region = "east";
40122     config.cursor = "e-resize";
40123     Roo.bootstrap.layout.Split.call(this, config);
40124     if(this.split){
40125         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40126         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40127         this.split.el.addClass("roo-layout-split-h");
40128     }
40129     
40130 };
40131 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40132     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40133     
40134     onRender : function(ctr, pos)
40135     {
40136         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40137         var size = this.config.initialSize || this.config.width;
40138         if(this.el && typeof size != "undefined"){
40139             this.el.setWidth(size);
40140         }
40141     
40142     },
40143     
40144     getBox : function(){
40145         if(this.collapsed){
40146             return this.collapsedEl.getBox();
40147         }
40148         var box = this.el.getBox();
40149         if(this.split){
40150             var sw = this.split.el.getWidth();
40151             box.width += sw;
40152             box.x -= sw;
40153         }
40154         return box;
40155     },
40156
40157     updateBox : function(box){
40158         if(this.split && !this.collapsed){
40159             var sw = this.split.el.getWidth();
40160             box.width -= sw;
40161             this.split.el.setLeft(box.x);
40162             this.split.el.setTop(box.y);
40163             this.split.el.setHeight(box.height);
40164             box.x += sw;
40165         }
40166         if(this.collapsed){
40167             this.updateBody(null, box.height);
40168         }
40169         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40170     }
40171 });
40172
40173 Roo.bootstrap.layout.West = function(config){
40174     config.region = "west";
40175     config.cursor = "w-resize";
40176     
40177     Roo.bootstrap.layout.Split.call(this, config);
40178     if(this.split){
40179         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40180         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40181         this.split.el.addClass("roo-layout-split-h");
40182     }
40183     
40184 };
40185 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40186     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40187     
40188     onRender: function(ctr, pos)
40189     {
40190         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40191         var size = this.config.initialSize || this.config.width;
40192         if(typeof size != "undefined"){
40193             this.el.setWidth(size);
40194         }
40195     },
40196     
40197     getBox : function(){
40198         if(this.collapsed){
40199             return this.collapsedEl.getBox();
40200         }
40201         var box = this.el.getBox();
40202         if (box.width == 0) {
40203             box.width = this.config.width; // kludge?
40204         }
40205         if(this.split){
40206             box.width += this.split.el.getWidth();
40207         }
40208         return box;
40209     },
40210     
40211     updateBox : function(box){
40212         if(this.split && !this.collapsed){
40213             var sw = this.split.el.getWidth();
40214             box.width -= sw;
40215             this.split.el.setLeft(box.x+box.width);
40216             this.split.el.setTop(box.y);
40217             this.split.el.setHeight(box.height);
40218         }
40219         if(this.collapsed){
40220             this.updateBody(null, box.height);
40221         }
40222         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40223     }
40224 });/*
40225  * Based on:
40226  * Ext JS Library 1.1.1
40227  * Copyright(c) 2006-2007, Ext JS, LLC.
40228  *
40229  * Originally Released Under LGPL - original licence link has changed is not relivant.
40230  *
40231  * Fork - LGPL
40232  * <script type="text/javascript">
40233  */
40234 /**
40235  * @class Roo.bootstrap.paenl.Content
40236  * @extends Roo.util.Observable
40237  * @children Roo.bootstrap.Component
40238  * @parent builder Roo.bootstrap.layout.Border
40239  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40240  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40241  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40242  * @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
40243  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40244  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40245  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40246  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40247  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40248  * @cfg {String} title          The title for this panel
40249  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40250  * @cfg {String} url            Calls {@link #setUrl} with this value
40251  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40252  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40253  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40254  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40255  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40256  * @cfg {Boolean} badges render the badges
40257  * @cfg {String} cls  extra classes to use  
40258  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40259  
40260  * @constructor
40261  * Create a new ContentPanel.
40262  * @param {String/Object} config A string to set only the title or a config object
40263  
40264  */
40265 Roo.bootstrap.panel.Content = function( config){
40266     
40267     this.tpl = config.tpl || false;
40268     
40269     var el = config.el;
40270     var content = config.content;
40271
40272     if(config.autoCreate){ // xtype is available if this is called from factory
40273         el = Roo.id();
40274     }
40275     this.el = Roo.get(el);
40276     if(!this.el && config && config.autoCreate){
40277         if(typeof config.autoCreate == "object"){
40278             if(!config.autoCreate.id){
40279                 config.autoCreate.id = config.id||el;
40280             }
40281             this.el = Roo.DomHelper.append(document.body,
40282                         config.autoCreate, true);
40283         }else{
40284             var elcfg =  {
40285                 tag: "div",
40286                 cls: (config.cls || '') +
40287                     (config.background ? ' bg-' + config.background : '') +
40288                     " roo-layout-inactive-content",
40289                 id: config.id||el
40290             };
40291             if (config.iframe) {
40292                 elcfg.cn = [
40293                     {
40294                         tag : 'iframe',
40295                         style : 'border: 0px',
40296                         src : 'about:blank'
40297                     }
40298                 ];
40299             }
40300               
40301             if (config.html) {
40302                 elcfg.html = config.html;
40303                 
40304             }
40305                         
40306             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40307             if (config.iframe) {
40308                 this.iframeEl = this.el.select('iframe',true).first();
40309             }
40310             
40311         }
40312     } 
40313     this.closable = false;
40314     this.loaded = false;
40315     this.active = false;
40316    
40317       
40318     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40319         
40320         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40321         
40322         this.wrapEl = this.el; //this.el.wrap();
40323         var ti = [];
40324         if (config.toolbar.items) {
40325             ti = config.toolbar.items ;
40326             delete config.toolbar.items ;
40327         }
40328         
40329         var nitems = [];
40330         this.toolbar.render(this.wrapEl, 'before');
40331         for(var i =0;i < ti.length;i++) {
40332           //  Roo.log(['add child', items[i]]);
40333             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40334         }
40335         this.toolbar.items = nitems;
40336         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40337         delete config.toolbar;
40338         
40339     }
40340     /*
40341     // xtype created footer. - not sure if will work as we normally have to render first..
40342     if (this.footer && !this.footer.el && this.footer.xtype) {
40343         if (!this.wrapEl) {
40344             this.wrapEl = this.el.wrap();
40345         }
40346     
40347         this.footer.container = this.wrapEl.createChild();
40348          
40349         this.footer = Roo.factory(this.footer, Roo);
40350         
40351     }
40352     */
40353     
40354      if(typeof config == "string"){
40355         this.title = config;
40356     }else{
40357         Roo.apply(this, config);
40358     }
40359     
40360     if(this.resizeEl){
40361         this.resizeEl = Roo.get(this.resizeEl, true);
40362     }else{
40363         this.resizeEl = this.el;
40364     }
40365     // handle view.xtype
40366     
40367  
40368     
40369     
40370     this.addEvents({
40371         /**
40372          * @event activate
40373          * Fires when this panel is activated. 
40374          * @param {Roo.ContentPanel} this
40375          */
40376         "activate" : true,
40377         /**
40378          * @event deactivate
40379          * Fires when this panel is activated. 
40380          * @param {Roo.ContentPanel} this
40381          */
40382         "deactivate" : true,
40383
40384         /**
40385          * @event resize
40386          * Fires when this panel is resized if fitToFrame is true.
40387          * @param {Roo.ContentPanel} this
40388          * @param {Number} width The width after any component adjustments
40389          * @param {Number} height The height after any component adjustments
40390          */
40391         "resize" : true,
40392         
40393          /**
40394          * @event render
40395          * Fires when this tab is created
40396          * @param {Roo.ContentPanel} this
40397          */
40398         "render" : true,
40399         
40400           /**
40401          * @event scroll
40402          * Fires when this content is scrolled
40403          * @param {Roo.ContentPanel} this
40404          * @param {Event} scrollEvent
40405          */
40406         "scroll" : true
40407         
40408         
40409         
40410     });
40411     
40412
40413     
40414     
40415     if(this.autoScroll && !this.iframe){
40416         this.resizeEl.setStyle("overflow", "auto");
40417         this.resizeEl.on('scroll', this.onScroll, this);
40418     } else {
40419         // fix randome scrolling
40420         //this.el.on('scroll', function() {
40421         //    Roo.log('fix random scolling');
40422         //    this.scrollTo('top',0); 
40423         //});
40424     }
40425     content = content || this.content;
40426     if(content){
40427         this.setContent(content);
40428     }
40429     if(config && config.url){
40430         this.setUrl(this.url, this.params, this.loadOnce);
40431     }
40432     
40433     
40434     
40435     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40436     
40437     if (this.view && typeof(this.view.xtype) != 'undefined') {
40438         this.view.el = this.el.appendChild(document.createElement("div"));
40439         this.view = Roo.factory(this.view); 
40440         this.view.render  &&  this.view.render(false, '');  
40441     }
40442     
40443     
40444     this.fireEvent('render', this);
40445 };
40446
40447 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40448     
40449     cls : '',
40450     background : '',
40451     
40452     tabTip : '',
40453     
40454     iframe : false,
40455     iframeEl : false,
40456     
40457     /* Resize Element - use this to work out scroll etc. */
40458     resizeEl : false,
40459     
40460     setRegion : function(region){
40461         this.region = region;
40462         this.setActiveClass(region && !this.background);
40463     },
40464     
40465     
40466     setActiveClass: function(state)
40467     {
40468         if(state){
40469            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40470            this.el.setStyle('position','relative');
40471         }else{
40472            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40473            this.el.setStyle('position', 'absolute');
40474         } 
40475     },
40476     
40477     /**
40478      * Returns the toolbar for this Panel if one was configured. 
40479      * @return {Roo.Toolbar} 
40480      */
40481     getToolbar : function(){
40482         return this.toolbar;
40483     },
40484     
40485     setActiveState : function(active)
40486     {
40487         this.active = active;
40488         this.setActiveClass(active);
40489         if(!active){
40490             if(this.fireEvent("deactivate", this) === false){
40491                 return false;
40492             }
40493             return true;
40494         }
40495         this.fireEvent("activate", this);
40496         return true;
40497     },
40498     /**
40499      * Updates this panel's element (not for iframe)
40500      * @param {String} content The new content
40501      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40502     */
40503     setContent : function(content, loadScripts){
40504         if (this.iframe) {
40505             return;
40506         }
40507         
40508         this.el.update(content, loadScripts);
40509     },
40510
40511     ignoreResize : function(w, h)
40512     {
40513         return false; // always resize?
40514         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40515             return true;
40516         }else{
40517             this.lastSize = {width: w, height: h};
40518             return false;
40519         }
40520     },
40521     /**
40522      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40523      * @return {Roo.UpdateManager} The UpdateManager
40524      */
40525     getUpdateManager : function(){
40526         if (this.iframe) {
40527             return false;
40528         }
40529         return this.el.getUpdateManager();
40530     },
40531      /**
40532      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40533      * Does not work with IFRAME contents
40534      * @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:
40535 <pre><code>
40536 panel.load({
40537     url: "your-url.php",
40538     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40539     callback: yourFunction,
40540     scope: yourObject, //(optional scope)
40541     discardUrl: false,
40542     nocache: false,
40543     text: "Loading...",
40544     timeout: 30,
40545     scripts: false
40546 });
40547 </code></pre>
40548      
40549      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40550      * 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.
40551      * @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}
40552      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40553      * @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.
40554      * @return {Roo.ContentPanel} this
40555      */
40556     load : function(){
40557         
40558         if (this.iframe) {
40559             return this;
40560         }
40561         
40562         var um = this.el.getUpdateManager();
40563         um.update.apply(um, arguments);
40564         return this;
40565     },
40566
40567
40568     /**
40569      * 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.
40570      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40571      * @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)
40572      * @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)
40573      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40574      */
40575     setUrl : function(url, params, loadOnce){
40576         if (this.iframe) {
40577             this.iframeEl.dom.src = url;
40578             return false;
40579         }
40580         
40581         if(this.refreshDelegate){
40582             this.removeListener("activate", this.refreshDelegate);
40583         }
40584         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40585         this.on("activate", this.refreshDelegate);
40586         return this.el.getUpdateManager();
40587     },
40588     
40589     _handleRefresh : function(url, params, loadOnce){
40590         if(!loadOnce || !this.loaded){
40591             var updater = this.el.getUpdateManager();
40592             updater.update(url, params, this._setLoaded.createDelegate(this));
40593         }
40594     },
40595     
40596     _setLoaded : function(){
40597         this.loaded = true;
40598     }, 
40599     
40600     /**
40601      * Returns this panel's id
40602      * @return {String} 
40603      */
40604     getId : function(){
40605         return this.el.id;
40606     },
40607     
40608     /** 
40609      * Returns this panel's element - used by regiosn to add.
40610      * @return {Roo.Element} 
40611      */
40612     getEl : function(){
40613         return this.wrapEl || this.el;
40614     },
40615     
40616    
40617     
40618     adjustForComponents : function(width, height)
40619     {
40620         //Roo.log('adjustForComponents ');
40621         if(this.resizeEl != this.el){
40622             width -= this.el.getFrameWidth('lr');
40623             height -= this.el.getFrameWidth('tb');
40624         }
40625         if(this.toolbar){
40626             var te = this.toolbar.getEl();
40627             te.setWidth(width);
40628             height -= te.getHeight();
40629         }
40630         if(this.footer){
40631             var te = this.footer.getEl();
40632             te.setWidth(width);
40633             height -= te.getHeight();
40634         }
40635         
40636         
40637         if(this.adjustments){
40638             width += this.adjustments[0];
40639             height += this.adjustments[1];
40640         }
40641         return {"width": width, "height": height};
40642     },
40643     
40644     setSize : function(width, height){
40645         if(this.fitToFrame && !this.ignoreResize(width, height)){
40646             if(this.fitContainer && this.resizeEl != this.el){
40647                 this.el.setSize(width, height);
40648             }
40649             var size = this.adjustForComponents(width, height);
40650             if (this.iframe) {
40651                 this.iframeEl.setSize(width,height);
40652             }
40653             
40654             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40655             this.fireEvent('resize', this, size.width, size.height);
40656             
40657             
40658         }
40659     },
40660     
40661     /**
40662      * Returns this panel's title
40663      * @return {String} 
40664      */
40665     getTitle : function(){
40666         
40667         if (typeof(this.title) != 'object') {
40668             return this.title;
40669         }
40670         
40671         var t = '';
40672         for (var k in this.title) {
40673             if (!this.title.hasOwnProperty(k)) {
40674                 continue;
40675             }
40676             
40677             if (k.indexOf('-') >= 0) {
40678                 var s = k.split('-');
40679                 for (var i = 0; i<s.length; i++) {
40680                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40681                 }
40682             } else {
40683                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40684             }
40685         }
40686         return t;
40687     },
40688     
40689     /**
40690      * Set this panel's title
40691      * @param {String} title
40692      */
40693     setTitle : function(title){
40694         this.title = title;
40695         if(this.region){
40696             this.region.updatePanelTitle(this, title);
40697         }
40698     },
40699     
40700     /**
40701      * Returns true is this panel was configured to be closable
40702      * @return {Boolean} 
40703      */
40704     isClosable : function(){
40705         return this.closable;
40706     },
40707     
40708     beforeSlide : function(){
40709         this.el.clip();
40710         this.resizeEl.clip();
40711     },
40712     
40713     afterSlide : function(){
40714         this.el.unclip();
40715         this.resizeEl.unclip();
40716     },
40717     
40718     /**
40719      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40720      *   Will fail silently if the {@link #setUrl} method has not been called.
40721      *   This does not activate the panel, just updates its content.
40722      */
40723     refresh : function(){
40724         if(this.refreshDelegate){
40725            this.loaded = false;
40726            this.refreshDelegate();
40727         }
40728     },
40729     
40730     /**
40731      * Destroys this panel
40732      */
40733     destroy : function(){
40734         this.el.removeAllListeners();
40735         var tempEl = document.createElement("span");
40736         tempEl.appendChild(this.el.dom);
40737         tempEl.innerHTML = "";
40738         this.el.remove();
40739         this.el = null;
40740     },
40741     
40742     /**
40743      * form - if the content panel contains a form - this is a reference to it.
40744      * @type {Roo.form.Form}
40745      */
40746     form : false,
40747     /**
40748      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40749      *    This contains a reference to it.
40750      * @type {Roo.View}
40751      */
40752     view : false,
40753     
40754       /**
40755      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40756      * <pre><code>
40757
40758 layout.addxtype({
40759        xtype : 'Form',
40760        items: [ .... ]
40761    }
40762 );
40763
40764 </code></pre>
40765      * @param {Object} cfg Xtype definition of item to add.
40766      */
40767     
40768     
40769     getChildContainer: function () {
40770         return this.getEl();
40771     },
40772     
40773     
40774     onScroll : function(e)
40775     {
40776         this.fireEvent('scroll', this, e);
40777     }
40778     
40779     
40780     /*
40781         var  ret = new Roo.factory(cfg);
40782         return ret;
40783         
40784         
40785         // add form..
40786         if (cfg.xtype.match(/^Form$/)) {
40787             
40788             var el;
40789             //if (this.footer) {
40790             //    el = this.footer.container.insertSibling(false, 'before');
40791             //} else {
40792                 el = this.el.createChild();
40793             //}
40794
40795             this.form = new  Roo.form.Form(cfg);
40796             
40797             
40798             if ( this.form.allItems.length) {
40799                 this.form.render(el.dom);
40800             }
40801             return this.form;
40802         }
40803         // should only have one of theses..
40804         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40805             // views.. should not be just added - used named prop 'view''
40806             
40807             cfg.el = this.el.appendChild(document.createElement("div"));
40808             // factory?
40809             
40810             var ret = new Roo.factory(cfg);
40811              
40812              ret.render && ret.render(false, ''); // render blank..
40813             this.view = ret;
40814             return ret;
40815         }
40816         return false;
40817     }
40818     \*/
40819 });
40820  
40821 /**
40822  * @class Roo.bootstrap.panel.Grid
40823  * @extends Roo.bootstrap.panel.Content
40824  * @constructor
40825  * Create a new GridPanel.
40826  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40827  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40828  * @param {Object} config A the config object
40829   
40830  */
40831
40832
40833
40834 Roo.bootstrap.panel.Grid = function(config)
40835 {
40836     
40837       
40838     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40839         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40840
40841     config.el = this.wrapper;
40842     //this.el = this.wrapper;
40843     
40844       if (config.container) {
40845         // ctor'ed from a Border/panel.grid
40846         
40847         
40848         this.wrapper.setStyle("overflow", "hidden");
40849         this.wrapper.addClass('roo-grid-container');
40850
40851     }
40852     
40853     
40854     if(config.toolbar){
40855         var tool_el = this.wrapper.createChild();    
40856         this.toolbar = Roo.factory(config.toolbar);
40857         var ti = [];
40858         if (config.toolbar.items) {
40859             ti = config.toolbar.items ;
40860             delete config.toolbar.items ;
40861         }
40862         
40863         var nitems = [];
40864         this.toolbar.render(tool_el);
40865         for(var i =0;i < ti.length;i++) {
40866           //  Roo.log(['add child', items[i]]);
40867             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40868         }
40869         this.toolbar.items = nitems;
40870         
40871         delete config.toolbar;
40872     }
40873     
40874     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40875     config.grid.scrollBody = true;;
40876     config.grid.monitorWindowResize = false; // turn off autosizing
40877     config.grid.autoHeight = false;
40878     config.grid.autoWidth = false;
40879     
40880     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40881     
40882     if (config.background) {
40883         // render grid on panel activation (if panel background)
40884         this.on('activate', function(gp) {
40885             if (!gp.grid.rendered) {
40886                 gp.grid.render(this.wrapper);
40887                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40888             }
40889         });
40890             
40891     } else {
40892         this.grid.render(this.wrapper);
40893         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40894
40895     }
40896     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40897     // ??? needed ??? config.el = this.wrapper;
40898     
40899     
40900     
40901   
40902     // xtype created footer. - not sure if will work as we normally have to render first..
40903     if (this.footer && !this.footer.el && this.footer.xtype) {
40904         
40905         var ctr = this.grid.getView().getFooterPanel(true);
40906         this.footer.dataSource = this.grid.dataSource;
40907         this.footer = Roo.factory(this.footer, Roo);
40908         this.footer.render(ctr);
40909         
40910     }
40911     
40912     
40913     
40914     
40915      
40916 };
40917
40918 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
40919 {
40920     // private
40921     is_resizing : false,
40922     
40923     getId : function(){
40924         return this.grid.id;
40925     },
40926     
40927     /**
40928      * Returns the grid for this panel
40929      * @return {Roo.bootstrap.Table} 
40930      */
40931     getGrid : function(){
40932         return this.grid;    
40933     },
40934     
40935     setSize : function(width, height)
40936     {
40937         if (this.is_resizing) {
40938             return;
40939         
40940         }
40941         this.is_resizing = true;
40942         if(!this.ignoreResize(width, height)){
40943             var grid = this.grid;
40944             var size = this.adjustForComponents(width, height);
40945             // tfoot is not a footer?
40946           
40947             
40948             var gridel = grid.getGridEl();
40949             gridel.setSize(size.width, size.height);
40950             
40951             var tbd = grid.getGridEl().select('tbody', true).first();
40952             var thd = grid.getGridEl().select('thead',true).first();
40953             var tbf= grid.getGridEl().select('tfoot', true).first();
40954
40955             if (tbf) {
40956                 size.height -= tbf.getHeight();
40957             }
40958             if (thd) {
40959                 size.height -= thd.getHeight();
40960             }
40961             
40962             tbd.setSize(size.width, size.height );
40963             // this is for the account management tab -seems to work there.
40964             var thd = grid.getGridEl().select('thead',true).first();
40965             //if (tbd) {
40966             //    tbd.setSize(size.width, size.height - thd.getHeight());
40967             //}
40968              
40969             grid.autoSize();
40970         }
40971         this.is_resizing = false;
40972     },
40973      
40974     
40975     
40976     beforeSlide : function(){
40977         this.grid.getView().scroller.clip();
40978     },
40979     
40980     afterSlide : function(){
40981         this.grid.getView().scroller.unclip();
40982     },
40983     
40984     destroy : function(){
40985         this.grid.destroy();
40986         delete this.grid;
40987         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40988     }
40989 });
40990
40991 /**
40992  * @class Roo.bootstrap.panel.Nest
40993  * @extends Roo.bootstrap.panel.Content
40994  * @constructor
40995  * Create a new Panel, that can contain a layout.Border.
40996  * 
40997  * 
40998  * @param {String/Object} config A string to set only the title or a config object
40999  */
41000 Roo.bootstrap.panel.Nest = function(config)
41001 {
41002     // construct with only one argument..
41003     /* FIXME - implement nicer consturctors
41004     if (layout.layout) {
41005         config = layout;
41006         layout = config.layout;
41007         delete config.layout;
41008     }
41009     if (layout.xtype && !layout.getEl) {
41010         // then layout needs constructing..
41011         layout = Roo.factory(layout, Roo);
41012     }
41013     */
41014     
41015     config.el =  config.layout.getEl();
41016     
41017     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41018     
41019     config.layout.monitorWindowResize = false; // turn off autosizing
41020     this.layout = config.layout;
41021     this.layout.getEl().addClass("roo-layout-nested-layout");
41022     this.layout.parent = this;
41023     
41024     
41025     
41026     
41027 };
41028
41029 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41030     /**
41031     * @cfg {Roo.BorderLayout} layout The layout for this panel
41032     */
41033     layout : false,
41034
41035     setSize : function(width, height){
41036         if(!this.ignoreResize(width, height)){
41037             var size = this.adjustForComponents(width, height);
41038             var el = this.layout.getEl();
41039             if (size.height < 1) {
41040                 el.setWidth(size.width);   
41041             } else {
41042                 el.setSize(size.width, size.height);
41043             }
41044             var touch = el.dom.offsetWidth;
41045             this.layout.layout();
41046             // ie requires a double layout on the first pass
41047             if(Roo.isIE && !this.initialized){
41048                 this.initialized = true;
41049                 this.layout.layout();
41050             }
41051         }
41052     },
41053     
41054     // activate all subpanels if not currently active..
41055     
41056     setActiveState : function(active){
41057         this.active = active;
41058         this.setActiveClass(active);
41059         
41060         if(!active){
41061             this.fireEvent("deactivate", this);
41062             return;
41063         }
41064         
41065         this.fireEvent("activate", this);
41066         // not sure if this should happen before or after..
41067         if (!this.layout) {
41068             return; // should not happen..
41069         }
41070         var reg = false;
41071         for (var r in this.layout.regions) {
41072             reg = this.layout.getRegion(r);
41073             if (reg.getActivePanel()) {
41074                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41075                 reg.setActivePanel(reg.getActivePanel());
41076                 continue;
41077             }
41078             if (!reg.panels.length) {
41079                 continue;
41080             }
41081             reg.showPanel(reg.getPanel(0));
41082         }
41083         
41084         
41085         
41086         
41087     },
41088     
41089     /**
41090      * Returns the nested BorderLayout for this panel
41091      * @return {Roo.BorderLayout} 
41092      */
41093     getLayout : function(){
41094         return this.layout;
41095     },
41096     
41097      /**
41098      * Adds a xtype elements to the layout of the nested panel
41099      * <pre><code>
41100
41101 panel.addxtype({
41102        xtype : 'ContentPanel',
41103        region: 'west',
41104        items: [ .... ]
41105    }
41106 );
41107
41108 panel.addxtype({
41109         xtype : 'NestedLayoutPanel',
41110         region: 'west',
41111         layout: {
41112            center: { },
41113            west: { }   
41114         },
41115         items : [ ... list of content panels or nested layout panels.. ]
41116    }
41117 );
41118 </code></pre>
41119      * @param {Object} cfg Xtype definition of item to add.
41120      */
41121     addxtype : function(cfg) {
41122         return this.layout.addxtype(cfg);
41123     
41124     }
41125 });/*
41126  * Based on:
41127  * Ext JS Library 1.1.1
41128  * Copyright(c) 2006-2007, Ext JS, LLC.
41129  *
41130  * Originally Released Under LGPL - original licence link has changed is not relivant.
41131  *
41132  * Fork - LGPL
41133  * <script type="text/javascript">
41134  */
41135 /**
41136  * @class Roo.TabPanel
41137  * @extends Roo.util.Observable
41138  * A lightweight tab container.
41139  * <br><br>
41140  * Usage:
41141  * <pre><code>
41142 // basic tabs 1, built from existing content
41143 var tabs = new Roo.TabPanel("tabs1");
41144 tabs.addTab("script", "View Script");
41145 tabs.addTab("markup", "View Markup");
41146 tabs.activate("script");
41147
41148 // more advanced tabs, built from javascript
41149 var jtabs = new Roo.TabPanel("jtabs");
41150 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41151
41152 // set up the UpdateManager
41153 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41154 var updater = tab2.getUpdateManager();
41155 updater.setDefaultUrl("ajax1.htm");
41156 tab2.on('activate', updater.refresh, updater, true);
41157
41158 // Use setUrl for Ajax loading
41159 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41160 tab3.setUrl("ajax2.htm", null, true);
41161
41162 // Disabled tab
41163 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41164 tab4.disable();
41165
41166 jtabs.activate("jtabs-1");
41167  * </code></pre>
41168  * @constructor
41169  * Create a new TabPanel.
41170  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41171  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41172  */
41173 Roo.bootstrap.panel.Tabs = function(config){
41174     /**
41175     * The container element for this TabPanel.
41176     * @type Roo.Element
41177     */
41178     this.el = Roo.get(config.el);
41179     delete config.el;
41180     if(config){
41181         if(typeof config == "boolean"){
41182             this.tabPosition = config ? "bottom" : "top";
41183         }else{
41184             Roo.apply(this, config);
41185         }
41186     }
41187     
41188     if(this.tabPosition == "bottom"){
41189         // if tabs are at the bottom = create the body first.
41190         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41191         this.el.addClass("roo-tabs-bottom");
41192     }
41193     // next create the tabs holders
41194     
41195     if (this.tabPosition == "west"){
41196         
41197         var reg = this.region; // fake it..
41198         while (reg) {
41199             if (!reg.mgr.parent) {
41200                 break;
41201             }
41202             reg = reg.mgr.parent.region;
41203         }
41204         Roo.log("got nest?");
41205         Roo.log(reg);
41206         if (reg.mgr.getRegion('west')) {
41207             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41208             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41209             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41210             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41211             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41212         
41213             
41214         }
41215         
41216         
41217     } else {
41218      
41219         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41220         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41221         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41222         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41223     }
41224     
41225     
41226     if(Roo.isIE){
41227         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41228     }
41229     
41230     // finally - if tabs are at the top, then create the body last..
41231     if(this.tabPosition != "bottom"){
41232         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41233          * @type Roo.Element
41234          */
41235         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41236         this.el.addClass("roo-tabs-top");
41237     }
41238     this.items = [];
41239
41240     this.bodyEl.setStyle("position", "relative");
41241
41242     this.active = null;
41243     this.activateDelegate = this.activate.createDelegate(this);
41244
41245     this.addEvents({
41246         /**
41247          * @event tabchange
41248          * Fires when the active tab changes
41249          * @param {Roo.TabPanel} this
41250          * @param {Roo.TabPanelItem} activePanel The new active tab
41251          */
41252         "tabchange": true,
41253         /**
41254          * @event beforetabchange
41255          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41256          * @param {Roo.TabPanel} this
41257          * @param {Object} e Set cancel to true on this object to cancel the tab change
41258          * @param {Roo.TabPanelItem} tab The tab being changed to
41259          */
41260         "beforetabchange" : true
41261     });
41262
41263     Roo.EventManager.onWindowResize(this.onResize, this);
41264     this.cpad = this.el.getPadding("lr");
41265     this.hiddenCount = 0;
41266
41267
41268     // toolbar on the tabbar support...
41269     if (this.toolbar) {
41270         alert("no toolbar support yet");
41271         this.toolbar  = false;
41272         /*
41273         var tcfg = this.toolbar;
41274         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41275         this.toolbar = new Roo.Toolbar(tcfg);
41276         if (Roo.isSafari) {
41277             var tbl = tcfg.container.child('table', true);
41278             tbl.setAttribute('width', '100%');
41279         }
41280         */
41281         
41282     }
41283    
41284
41285
41286     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41287 };
41288
41289 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41290     /*
41291      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41292      */
41293     tabPosition : "top",
41294     /*
41295      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41296      */
41297     currentTabWidth : 0,
41298     /*
41299      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41300      */
41301     minTabWidth : 40,
41302     /*
41303      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41304      */
41305     maxTabWidth : 250,
41306     /*
41307      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41308      */
41309     preferredTabWidth : 175,
41310     /*
41311      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41312      */
41313     resizeTabs : false,
41314     /*
41315      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41316      */
41317     monitorResize : true,
41318     /*
41319      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41320      */
41321     toolbar : false,  // set by caller..
41322     
41323     region : false, /// set by caller
41324     
41325     disableTooltips : true, // not used yet...
41326
41327     /**
41328      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41329      * @param {String} id The id of the div to use <b>or create</b>
41330      * @param {String} text The text for the tab
41331      * @param {String} content (optional) Content to put in the TabPanelItem body
41332      * @param {Boolean} closable (optional) True to create a close icon on the tab
41333      * @return {Roo.TabPanelItem} The created TabPanelItem
41334      */
41335     addTab : function(id, text, content, closable, tpl)
41336     {
41337         var item = new Roo.bootstrap.panel.TabItem({
41338             panel: this,
41339             id : id,
41340             text : text,
41341             closable : closable,
41342             tpl : tpl
41343         });
41344         this.addTabItem(item);
41345         if(content){
41346             item.setContent(content);
41347         }
41348         return item;
41349     },
41350
41351     /**
41352      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41353      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41354      * @return {Roo.TabPanelItem}
41355      */
41356     getTab : function(id){
41357         return this.items[id];
41358     },
41359
41360     /**
41361      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41362      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41363      */
41364     hideTab : function(id){
41365         var t = this.items[id];
41366         if(!t.isHidden()){
41367            t.setHidden(true);
41368            this.hiddenCount++;
41369            this.autoSizeTabs();
41370         }
41371     },
41372
41373     /**
41374      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41375      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41376      */
41377     unhideTab : function(id){
41378         var t = this.items[id];
41379         if(t.isHidden()){
41380            t.setHidden(false);
41381            this.hiddenCount--;
41382            this.autoSizeTabs();
41383         }
41384     },
41385
41386     /**
41387      * Adds an existing {@link Roo.TabPanelItem}.
41388      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41389      */
41390     addTabItem : function(item)
41391     {
41392         this.items[item.id] = item;
41393         this.items.push(item);
41394         this.autoSizeTabs();
41395       //  if(this.resizeTabs){
41396     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41397   //         this.autoSizeTabs();
41398 //        }else{
41399 //            item.autoSize();
41400        // }
41401     },
41402
41403     /**
41404      * Removes a {@link Roo.TabPanelItem}.
41405      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41406      */
41407     removeTab : function(id){
41408         var items = this.items;
41409         var tab = items[id];
41410         if(!tab) { return; }
41411         var index = items.indexOf(tab);
41412         if(this.active == tab && items.length > 1){
41413             var newTab = this.getNextAvailable(index);
41414             if(newTab) {
41415                 newTab.activate();
41416             }
41417         }
41418         this.stripEl.dom.removeChild(tab.pnode.dom);
41419         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41420             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41421         }
41422         items.splice(index, 1);
41423         delete this.items[tab.id];
41424         tab.fireEvent("close", tab);
41425         tab.purgeListeners();
41426         this.autoSizeTabs();
41427     },
41428
41429     getNextAvailable : function(start){
41430         var items = this.items;
41431         var index = start;
41432         // look for a next tab that will slide over to
41433         // replace the one being removed
41434         while(index < items.length){
41435             var item = items[++index];
41436             if(item && !item.isHidden()){
41437                 return item;
41438             }
41439         }
41440         // if one isn't found select the previous tab (on the left)
41441         index = start;
41442         while(index >= 0){
41443             var item = items[--index];
41444             if(item && !item.isHidden()){
41445                 return item;
41446             }
41447         }
41448         return null;
41449     },
41450
41451     /**
41452      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41453      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41454      */
41455     disableTab : function(id){
41456         var tab = this.items[id];
41457         if(tab && this.active != tab){
41458             tab.disable();
41459         }
41460     },
41461
41462     /**
41463      * Enables a {@link Roo.TabPanelItem} that is disabled.
41464      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41465      */
41466     enableTab : function(id){
41467         var tab = this.items[id];
41468         tab.enable();
41469     },
41470
41471     /**
41472      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41473      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41474      * @return {Roo.TabPanelItem} The TabPanelItem.
41475      */
41476     activate : function(id)
41477     {
41478         //Roo.log('activite:'  + id);
41479         
41480         var tab = this.items[id];
41481         if(!tab){
41482             return null;
41483         }
41484         if(tab == this.active || tab.disabled){
41485             return tab;
41486         }
41487         var e = {};
41488         this.fireEvent("beforetabchange", this, e, tab);
41489         if(e.cancel !== true && !tab.disabled){
41490             if(this.active){
41491                 this.active.hide();
41492             }
41493             this.active = this.items[id];
41494             this.active.show();
41495             this.fireEvent("tabchange", this, this.active);
41496         }
41497         return tab;
41498     },
41499
41500     /**
41501      * Gets the active {@link Roo.TabPanelItem}.
41502      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41503      */
41504     getActiveTab : function(){
41505         return this.active;
41506     },
41507
41508     /**
41509      * Updates the tab body element to fit the height of the container element
41510      * for overflow scrolling
41511      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41512      */
41513     syncHeight : function(targetHeight){
41514         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41515         var bm = this.bodyEl.getMargins();
41516         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41517         this.bodyEl.setHeight(newHeight);
41518         return newHeight;
41519     },
41520
41521     onResize : function(){
41522         if(this.monitorResize){
41523             this.autoSizeTabs();
41524         }
41525     },
41526
41527     /**
41528      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41529      */
41530     beginUpdate : function(){
41531         this.updating = true;
41532     },
41533
41534     /**
41535      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41536      */
41537     endUpdate : function(){
41538         this.updating = false;
41539         this.autoSizeTabs();
41540     },
41541
41542     /**
41543      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41544      */
41545     autoSizeTabs : function()
41546     {
41547         var count = this.items.length;
41548         var vcount = count - this.hiddenCount;
41549         
41550         if (vcount < 2) {
41551             this.stripEl.hide();
41552         } else {
41553             this.stripEl.show();
41554         }
41555         
41556         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41557             return;
41558         }
41559         
41560         
41561         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41562         var availWidth = Math.floor(w / vcount);
41563         var b = this.stripBody;
41564         if(b.getWidth() > w){
41565             var tabs = this.items;
41566             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41567             if(availWidth < this.minTabWidth){
41568                 /*if(!this.sleft){    // incomplete scrolling code
41569                     this.createScrollButtons();
41570                 }
41571                 this.showScroll();
41572                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41573             }
41574         }else{
41575             if(this.currentTabWidth < this.preferredTabWidth){
41576                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41577             }
41578         }
41579     },
41580
41581     /**
41582      * Returns the number of tabs in this TabPanel.
41583      * @return {Number}
41584      */
41585      getCount : function(){
41586          return this.items.length;
41587      },
41588
41589     /**
41590      * Resizes all the tabs to the passed width
41591      * @param {Number} The new width
41592      */
41593     setTabWidth : function(width){
41594         this.currentTabWidth = width;
41595         for(var i = 0, len = this.items.length; i < len; i++) {
41596                 if(!this.items[i].isHidden()) {
41597                 this.items[i].setWidth(width);
41598             }
41599         }
41600     },
41601
41602     /**
41603      * Destroys this TabPanel
41604      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41605      */
41606     destroy : function(removeEl){
41607         Roo.EventManager.removeResizeListener(this.onResize, this);
41608         for(var i = 0, len = this.items.length; i < len; i++){
41609             this.items[i].purgeListeners();
41610         }
41611         if(removeEl === true){
41612             this.el.update("");
41613             this.el.remove();
41614         }
41615     },
41616     
41617     createStrip : function(container)
41618     {
41619         var strip = document.createElement("nav");
41620         strip.className = Roo.bootstrap.version == 4 ?
41621             "navbar-light bg-light" : 
41622             "navbar navbar-default"; //"x-tabs-wrap";
41623         container.appendChild(strip);
41624         return strip;
41625     },
41626     
41627     createStripList : function(strip)
41628     {
41629         // div wrapper for retard IE
41630         // returns the "tr" element.
41631         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41632         //'<div class="x-tabs-strip-wrap">'+
41633           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41634           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41635         return strip.firstChild; //.firstChild.firstChild.firstChild;
41636     },
41637     createBody : function(container)
41638     {
41639         var body = document.createElement("div");
41640         Roo.id(body, "tab-body");
41641         //Roo.fly(body).addClass("x-tabs-body");
41642         Roo.fly(body).addClass("tab-content");
41643         container.appendChild(body);
41644         return body;
41645     },
41646     createItemBody :function(bodyEl, id){
41647         var body = Roo.getDom(id);
41648         if(!body){
41649             body = document.createElement("div");
41650             body.id = id;
41651         }
41652         //Roo.fly(body).addClass("x-tabs-item-body");
41653         Roo.fly(body).addClass("tab-pane");
41654          bodyEl.insertBefore(body, bodyEl.firstChild);
41655         return body;
41656     },
41657     /** @private */
41658     createStripElements :  function(stripEl, text, closable, tpl)
41659     {
41660         var td = document.createElement("li"); // was td..
41661         td.className = 'nav-item';
41662         
41663         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41664         
41665         
41666         stripEl.appendChild(td);
41667         /*if(closable){
41668             td.className = "x-tabs-closable";
41669             if(!this.closeTpl){
41670                 this.closeTpl = new Roo.Template(
41671                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41672                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41673                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41674                 );
41675             }
41676             var el = this.closeTpl.overwrite(td, {"text": text});
41677             var close = el.getElementsByTagName("div")[0];
41678             var inner = el.getElementsByTagName("em")[0];
41679             return {"el": el, "close": close, "inner": inner};
41680         } else {
41681         */
41682         // not sure what this is..
41683 //            if(!this.tabTpl){
41684                 //this.tabTpl = new Roo.Template(
41685                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41686                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41687                 //);
41688 //                this.tabTpl = new Roo.Template(
41689 //                   '<a href="#">' +
41690 //                   '<span unselectable="on"' +
41691 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41692 //                            ' >{text}</span></a>'
41693 //                );
41694 //                
41695 //            }
41696
41697
41698             var template = tpl || this.tabTpl || false;
41699             
41700             if(!template){
41701                 template =  new Roo.Template(
41702                         Roo.bootstrap.version == 4 ? 
41703                             (
41704                                 '<a class="nav-link" href="#" unselectable="on"' +
41705                                      (this.disableTooltips ? '' : ' title="{text}"') +
41706                                      ' >{text}</a>'
41707                             ) : (
41708                                 '<a class="nav-link" href="#">' +
41709                                 '<span unselectable="on"' +
41710                                          (this.disableTooltips ? '' : ' title="{text}"') +
41711                                     ' >{text}</span></a>'
41712                             )
41713                 );
41714             }
41715             
41716             switch (typeof(template)) {
41717                 case 'object' :
41718                     break;
41719                 case 'string' :
41720                     template = new Roo.Template(template);
41721                     break;
41722                 default :
41723                     break;
41724             }
41725             
41726             var el = template.overwrite(td, {"text": text});
41727             
41728             var inner = el.getElementsByTagName("span")[0];
41729             
41730             return {"el": el, "inner": inner};
41731             
41732     }
41733         
41734     
41735 });
41736
41737 /**
41738  * @class Roo.TabPanelItem
41739  * @extends Roo.util.Observable
41740  * Represents an individual item (tab plus body) in a TabPanel.
41741  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41742  * @param {String} id The id of this TabPanelItem
41743  * @param {String} text The text for the tab of this TabPanelItem
41744  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41745  */
41746 Roo.bootstrap.panel.TabItem = function(config){
41747     /**
41748      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41749      * @type Roo.TabPanel
41750      */
41751     this.tabPanel = config.panel;
41752     /**
41753      * The id for this TabPanelItem
41754      * @type String
41755      */
41756     this.id = config.id;
41757     /** @private */
41758     this.disabled = false;
41759     /** @private */
41760     this.text = config.text;
41761     /** @private */
41762     this.loaded = false;
41763     this.closable = config.closable;
41764
41765     /**
41766      * The body element for this TabPanelItem.
41767      * @type Roo.Element
41768      */
41769     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41770     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41771     this.bodyEl.setStyle("display", "block");
41772     this.bodyEl.setStyle("zoom", "1");
41773     //this.hideAction();
41774
41775     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41776     /** @private */
41777     this.el = Roo.get(els.el);
41778     this.inner = Roo.get(els.inner, true);
41779      this.textEl = Roo.bootstrap.version == 4 ?
41780         this.el : Roo.get(this.el.dom.firstChild, true);
41781
41782     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41783     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41784
41785     
41786 //    this.el.on("mousedown", this.onTabMouseDown, this);
41787     this.el.on("click", this.onTabClick, this);
41788     /** @private */
41789     if(config.closable){
41790         var c = Roo.get(els.close, true);
41791         c.dom.title = this.closeText;
41792         c.addClassOnOver("close-over");
41793         c.on("click", this.closeClick, this);
41794      }
41795
41796     this.addEvents({
41797          /**
41798          * @event activate
41799          * Fires when this tab becomes the active tab.
41800          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41801          * @param {Roo.TabPanelItem} this
41802          */
41803         "activate": true,
41804         /**
41805          * @event beforeclose
41806          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41807          * @param {Roo.TabPanelItem} this
41808          * @param {Object} e Set cancel to true on this object to cancel the close.
41809          */
41810         "beforeclose": true,
41811         /**
41812          * @event close
41813          * Fires when this tab is closed.
41814          * @param {Roo.TabPanelItem} this
41815          */
41816          "close": true,
41817         /**
41818          * @event deactivate
41819          * Fires when this tab is no longer the active tab.
41820          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41821          * @param {Roo.TabPanelItem} this
41822          */
41823          "deactivate" : true
41824     });
41825     this.hidden = false;
41826
41827     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41828 };
41829
41830 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41831            {
41832     purgeListeners : function(){
41833        Roo.util.Observable.prototype.purgeListeners.call(this);
41834        this.el.removeAllListeners();
41835     },
41836     /**
41837      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41838      */
41839     show : function(){
41840         this.status_node.addClass("active");
41841         this.showAction();
41842         if(Roo.isOpera){
41843             this.tabPanel.stripWrap.repaint();
41844         }
41845         this.fireEvent("activate", this.tabPanel, this);
41846     },
41847
41848     /**
41849      * Returns true if this tab is the active tab.
41850      * @return {Boolean}
41851      */
41852     isActive : function(){
41853         return this.tabPanel.getActiveTab() == this;
41854     },
41855
41856     /**
41857      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41858      */
41859     hide : function(){
41860         this.status_node.removeClass("active");
41861         this.hideAction();
41862         this.fireEvent("deactivate", this.tabPanel, this);
41863     },
41864
41865     hideAction : function(){
41866         this.bodyEl.hide();
41867         this.bodyEl.setStyle("position", "absolute");
41868         this.bodyEl.setLeft("-20000px");
41869         this.bodyEl.setTop("-20000px");
41870     },
41871
41872     showAction : function(){
41873         this.bodyEl.setStyle("position", "relative");
41874         this.bodyEl.setTop("");
41875         this.bodyEl.setLeft("");
41876         this.bodyEl.show();
41877     },
41878
41879     /**
41880      * Set the tooltip for the tab.
41881      * @param {String} tooltip The tab's tooltip
41882      */
41883     setTooltip : function(text){
41884         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41885             this.textEl.dom.qtip = text;
41886             this.textEl.dom.removeAttribute('title');
41887         }else{
41888             this.textEl.dom.title = text;
41889         }
41890     },
41891
41892     onTabClick : function(e){
41893         e.preventDefault();
41894         this.tabPanel.activate(this.id);
41895     },
41896
41897     onTabMouseDown : function(e){
41898         e.preventDefault();
41899         this.tabPanel.activate(this.id);
41900     },
41901 /*
41902     getWidth : function(){
41903         return this.inner.getWidth();
41904     },
41905
41906     setWidth : function(width){
41907         var iwidth = width - this.linode.getPadding("lr");
41908         this.inner.setWidth(iwidth);
41909         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41910         this.linode.setWidth(width);
41911     },
41912 */
41913     /**
41914      * Show or hide the tab
41915      * @param {Boolean} hidden True to hide or false to show.
41916      */
41917     setHidden : function(hidden){
41918         this.hidden = hidden;
41919         this.linode.setStyle("display", hidden ? "none" : "");
41920     },
41921
41922     /**
41923      * Returns true if this tab is "hidden"
41924      * @return {Boolean}
41925      */
41926     isHidden : function(){
41927         return this.hidden;
41928     },
41929
41930     /**
41931      * Returns the text for this tab
41932      * @return {String}
41933      */
41934     getText : function(){
41935         return this.text;
41936     },
41937     /*
41938     autoSize : function(){
41939         //this.el.beginMeasure();
41940         this.textEl.setWidth(1);
41941         /*
41942          *  #2804 [new] Tabs in Roojs
41943          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41944          */
41945         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41946         //this.el.endMeasure();
41947     //},
41948
41949     /**
41950      * Sets the text for the tab (Note: this also sets the tooltip text)
41951      * @param {String} text The tab's text and tooltip
41952      */
41953     setText : function(text){
41954         this.text = text;
41955         this.textEl.update(text);
41956         this.setTooltip(text);
41957         //if(!this.tabPanel.resizeTabs){
41958         //    this.autoSize();
41959         //}
41960     },
41961     /**
41962      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41963      */
41964     activate : function(){
41965         this.tabPanel.activate(this.id);
41966     },
41967
41968     /**
41969      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41970      */
41971     disable : function(){
41972         if(this.tabPanel.active != this){
41973             this.disabled = true;
41974             this.status_node.addClass("disabled");
41975         }
41976     },
41977
41978     /**
41979      * Enables this TabPanelItem if it was previously disabled.
41980      */
41981     enable : function(){
41982         this.disabled = false;
41983         this.status_node.removeClass("disabled");
41984     },
41985
41986     /**
41987      * Sets the content for this TabPanelItem.
41988      * @param {String} content The content
41989      * @param {Boolean} loadScripts true to look for and load scripts
41990      */
41991     setContent : function(content, loadScripts){
41992         this.bodyEl.update(content, loadScripts);
41993     },
41994
41995     /**
41996      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41997      * @return {Roo.UpdateManager} The UpdateManager
41998      */
41999     getUpdateManager : function(){
42000         return this.bodyEl.getUpdateManager();
42001     },
42002
42003     /**
42004      * Set a URL to be used to load the content for this TabPanelItem.
42005      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42006      * @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)
42007      * @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)
42008      * @return {Roo.UpdateManager} The UpdateManager
42009      */
42010     setUrl : function(url, params, loadOnce){
42011         if(this.refreshDelegate){
42012             this.un('activate', this.refreshDelegate);
42013         }
42014         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42015         this.on("activate", this.refreshDelegate);
42016         return this.bodyEl.getUpdateManager();
42017     },
42018
42019     /** @private */
42020     _handleRefresh : function(url, params, loadOnce){
42021         if(!loadOnce || !this.loaded){
42022             var updater = this.bodyEl.getUpdateManager();
42023             updater.update(url, params, this._setLoaded.createDelegate(this));
42024         }
42025     },
42026
42027     /**
42028      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42029      *   Will fail silently if the setUrl method has not been called.
42030      *   This does not activate the panel, just updates its content.
42031      */
42032     refresh : function(){
42033         if(this.refreshDelegate){
42034            this.loaded = false;
42035            this.refreshDelegate();
42036         }
42037     },
42038
42039     /** @private */
42040     _setLoaded : function(){
42041         this.loaded = true;
42042     },
42043
42044     /** @private */
42045     closeClick : function(e){
42046         var o = {};
42047         e.stopEvent();
42048         this.fireEvent("beforeclose", this, o);
42049         if(o.cancel !== true){
42050             this.tabPanel.removeTab(this.id);
42051         }
42052     },
42053     /**
42054      * The text displayed in the tooltip for the close icon.
42055      * @type String
42056      */
42057     closeText : "Close this tab"
42058 });
42059 /**
42060 *    This script refer to:
42061 *    Title: International Telephone Input
42062 *    Author: Jack O'Connor
42063 *    Code version:  v12.1.12
42064 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42065 **/
42066
42067 Roo.bootstrap.form.PhoneInputData = function() {
42068     var d = [
42069       [
42070         "Afghanistan (‫افغانستان‬‎)",
42071         "af",
42072         "93"
42073       ],
42074       [
42075         "Albania (Shqipëri)",
42076         "al",
42077         "355"
42078       ],
42079       [
42080         "Algeria (‫الجزائر‬‎)",
42081         "dz",
42082         "213"
42083       ],
42084       [
42085         "American Samoa",
42086         "as",
42087         "1684"
42088       ],
42089       [
42090         "Andorra",
42091         "ad",
42092         "376"
42093       ],
42094       [
42095         "Angola",
42096         "ao",
42097         "244"
42098       ],
42099       [
42100         "Anguilla",
42101         "ai",
42102         "1264"
42103       ],
42104       [
42105         "Antigua and Barbuda",
42106         "ag",
42107         "1268"
42108       ],
42109       [
42110         "Argentina",
42111         "ar",
42112         "54"
42113       ],
42114       [
42115         "Armenia (Հայաստան)",
42116         "am",
42117         "374"
42118       ],
42119       [
42120         "Aruba",
42121         "aw",
42122         "297"
42123       ],
42124       [
42125         "Australia",
42126         "au",
42127         "61",
42128         0
42129       ],
42130       [
42131         "Austria (Österreich)",
42132         "at",
42133         "43"
42134       ],
42135       [
42136         "Azerbaijan (Azərbaycan)",
42137         "az",
42138         "994"
42139       ],
42140       [
42141         "Bahamas",
42142         "bs",
42143         "1242"
42144       ],
42145       [
42146         "Bahrain (‫البحرين‬‎)",
42147         "bh",
42148         "973"
42149       ],
42150       [
42151         "Bangladesh (বাংলাদেশ)",
42152         "bd",
42153         "880"
42154       ],
42155       [
42156         "Barbados",
42157         "bb",
42158         "1246"
42159       ],
42160       [
42161         "Belarus (Беларусь)",
42162         "by",
42163         "375"
42164       ],
42165       [
42166         "Belgium (België)",
42167         "be",
42168         "32"
42169       ],
42170       [
42171         "Belize",
42172         "bz",
42173         "501"
42174       ],
42175       [
42176         "Benin (Bénin)",
42177         "bj",
42178         "229"
42179       ],
42180       [
42181         "Bermuda",
42182         "bm",
42183         "1441"
42184       ],
42185       [
42186         "Bhutan (འབྲུག)",
42187         "bt",
42188         "975"
42189       ],
42190       [
42191         "Bolivia",
42192         "bo",
42193         "591"
42194       ],
42195       [
42196         "Bosnia and Herzegovina (Босна и Херцеговина)",
42197         "ba",
42198         "387"
42199       ],
42200       [
42201         "Botswana",
42202         "bw",
42203         "267"
42204       ],
42205       [
42206         "Brazil (Brasil)",
42207         "br",
42208         "55"
42209       ],
42210       [
42211         "British Indian Ocean Territory",
42212         "io",
42213         "246"
42214       ],
42215       [
42216         "British Virgin Islands",
42217         "vg",
42218         "1284"
42219       ],
42220       [
42221         "Brunei",
42222         "bn",
42223         "673"
42224       ],
42225       [
42226         "Bulgaria (България)",
42227         "bg",
42228         "359"
42229       ],
42230       [
42231         "Burkina Faso",
42232         "bf",
42233         "226"
42234       ],
42235       [
42236         "Burundi (Uburundi)",
42237         "bi",
42238         "257"
42239       ],
42240       [
42241         "Cambodia (កម្ពុជា)",
42242         "kh",
42243         "855"
42244       ],
42245       [
42246         "Cameroon (Cameroun)",
42247         "cm",
42248         "237"
42249       ],
42250       [
42251         "Canada",
42252         "ca",
42253         "1",
42254         1,
42255         ["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"]
42256       ],
42257       [
42258         "Cape Verde (Kabu Verdi)",
42259         "cv",
42260         "238"
42261       ],
42262       [
42263         "Caribbean Netherlands",
42264         "bq",
42265         "599",
42266         1
42267       ],
42268       [
42269         "Cayman Islands",
42270         "ky",
42271         "1345"
42272       ],
42273       [
42274         "Central African Republic (République centrafricaine)",
42275         "cf",
42276         "236"
42277       ],
42278       [
42279         "Chad (Tchad)",
42280         "td",
42281         "235"
42282       ],
42283       [
42284         "Chile",
42285         "cl",
42286         "56"
42287       ],
42288       [
42289         "China (中国)",
42290         "cn",
42291         "86"
42292       ],
42293       [
42294         "Christmas Island",
42295         "cx",
42296         "61",
42297         2
42298       ],
42299       [
42300         "Cocos (Keeling) Islands",
42301         "cc",
42302         "61",
42303         1
42304       ],
42305       [
42306         "Colombia",
42307         "co",
42308         "57"
42309       ],
42310       [
42311         "Comoros (‫جزر القمر‬‎)",
42312         "km",
42313         "269"
42314       ],
42315       [
42316         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42317         "cd",
42318         "243"
42319       ],
42320       [
42321         "Congo (Republic) (Congo-Brazzaville)",
42322         "cg",
42323         "242"
42324       ],
42325       [
42326         "Cook Islands",
42327         "ck",
42328         "682"
42329       ],
42330       [
42331         "Costa Rica",
42332         "cr",
42333         "506"
42334       ],
42335       [
42336         "Côte d’Ivoire",
42337         "ci",
42338         "225"
42339       ],
42340       [
42341         "Croatia (Hrvatska)",
42342         "hr",
42343         "385"
42344       ],
42345       [
42346         "Cuba",
42347         "cu",
42348         "53"
42349       ],
42350       [
42351         "Curaçao",
42352         "cw",
42353         "599",
42354         0
42355       ],
42356       [
42357         "Cyprus (Κύπρος)",
42358         "cy",
42359         "357"
42360       ],
42361       [
42362         "Czech Republic (Česká republika)",
42363         "cz",
42364         "420"
42365       ],
42366       [
42367         "Denmark (Danmark)",
42368         "dk",
42369         "45"
42370       ],
42371       [
42372         "Djibouti",
42373         "dj",
42374         "253"
42375       ],
42376       [
42377         "Dominica",
42378         "dm",
42379         "1767"
42380       ],
42381       [
42382         "Dominican Republic (República Dominicana)",
42383         "do",
42384         "1",
42385         2,
42386         ["809", "829", "849"]
42387       ],
42388       [
42389         "Ecuador",
42390         "ec",
42391         "593"
42392       ],
42393       [
42394         "Egypt (‫مصر‬‎)",
42395         "eg",
42396         "20"
42397       ],
42398       [
42399         "El Salvador",
42400         "sv",
42401         "503"
42402       ],
42403       [
42404         "Equatorial Guinea (Guinea Ecuatorial)",
42405         "gq",
42406         "240"
42407       ],
42408       [
42409         "Eritrea",
42410         "er",
42411         "291"
42412       ],
42413       [
42414         "Estonia (Eesti)",
42415         "ee",
42416         "372"
42417       ],
42418       [
42419         "Ethiopia",
42420         "et",
42421         "251"
42422       ],
42423       [
42424         "Falkland Islands (Islas Malvinas)",
42425         "fk",
42426         "500"
42427       ],
42428       [
42429         "Faroe Islands (Føroyar)",
42430         "fo",
42431         "298"
42432       ],
42433       [
42434         "Fiji",
42435         "fj",
42436         "679"
42437       ],
42438       [
42439         "Finland (Suomi)",
42440         "fi",
42441         "358",
42442         0
42443       ],
42444       [
42445         "France",
42446         "fr",
42447         "33"
42448       ],
42449       [
42450         "French Guiana (Guyane française)",
42451         "gf",
42452         "594"
42453       ],
42454       [
42455         "French Polynesia (Polynésie française)",
42456         "pf",
42457         "689"
42458       ],
42459       [
42460         "Gabon",
42461         "ga",
42462         "241"
42463       ],
42464       [
42465         "Gambia",
42466         "gm",
42467         "220"
42468       ],
42469       [
42470         "Georgia (საქართველო)",
42471         "ge",
42472         "995"
42473       ],
42474       [
42475         "Germany (Deutschland)",
42476         "de",
42477         "49"
42478       ],
42479       [
42480         "Ghana (Gaana)",
42481         "gh",
42482         "233"
42483       ],
42484       [
42485         "Gibraltar",
42486         "gi",
42487         "350"
42488       ],
42489       [
42490         "Greece (Ελλάδα)",
42491         "gr",
42492         "30"
42493       ],
42494       [
42495         "Greenland (Kalaallit Nunaat)",
42496         "gl",
42497         "299"
42498       ],
42499       [
42500         "Grenada",
42501         "gd",
42502         "1473"
42503       ],
42504       [
42505         "Guadeloupe",
42506         "gp",
42507         "590",
42508         0
42509       ],
42510       [
42511         "Guam",
42512         "gu",
42513         "1671"
42514       ],
42515       [
42516         "Guatemala",
42517         "gt",
42518         "502"
42519       ],
42520       [
42521         "Guernsey",
42522         "gg",
42523         "44",
42524         1
42525       ],
42526       [
42527         "Guinea (Guinée)",
42528         "gn",
42529         "224"
42530       ],
42531       [
42532         "Guinea-Bissau (Guiné Bissau)",
42533         "gw",
42534         "245"
42535       ],
42536       [
42537         "Guyana",
42538         "gy",
42539         "592"
42540       ],
42541       [
42542         "Haiti",
42543         "ht",
42544         "509"
42545       ],
42546       [
42547         "Honduras",
42548         "hn",
42549         "504"
42550       ],
42551       [
42552         "Hong Kong (香港)",
42553         "hk",
42554         "852"
42555       ],
42556       [
42557         "Hungary (Magyarország)",
42558         "hu",
42559         "36"
42560       ],
42561       [
42562         "Iceland (Ísland)",
42563         "is",
42564         "354"
42565       ],
42566       [
42567         "India (भारत)",
42568         "in",
42569         "91"
42570       ],
42571       [
42572         "Indonesia",
42573         "id",
42574         "62"
42575       ],
42576       [
42577         "Iran (‫ایران‬‎)",
42578         "ir",
42579         "98"
42580       ],
42581       [
42582         "Iraq (‫العراق‬‎)",
42583         "iq",
42584         "964"
42585       ],
42586       [
42587         "Ireland",
42588         "ie",
42589         "353"
42590       ],
42591       [
42592         "Isle of Man",
42593         "im",
42594         "44",
42595         2
42596       ],
42597       [
42598         "Israel (‫ישראל‬‎)",
42599         "il",
42600         "972"
42601       ],
42602       [
42603         "Italy (Italia)",
42604         "it",
42605         "39",
42606         0
42607       ],
42608       [
42609         "Jamaica",
42610         "jm",
42611         "1876"
42612       ],
42613       [
42614         "Japan (日本)",
42615         "jp",
42616         "81"
42617       ],
42618       [
42619         "Jersey",
42620         "je",
42621         "44",
42622         3
42623       ],
42624       [
42625         "Jordan (‫الأردن‬‎)",
42626         "jo",
42627         "962"
42628       ],
42629       [
42630         "Kazakhstan (Казахстан)",
42631         "kz",
42632         "7",
42633         1
42634       ],
42635       [
42636         "Kenya",
42637         "ke",
42638         "254"
42639       ],
42640       [
42641         "Kiribati",
42642         "ki",
42643         "686"
42644       ],
42645       [
42646         "Kosovo",
42647         "xk",
42648         "383"
42649       ],
42650       [
42651         "Kuwait (‫الكويت‬‎)",
42652         "kw",
42653         "965"
42654       ],
42655       [
42656         "Kyrgyzstan (Кыргызстан)",
42657         "kg",
42658         "996"
42659       ],
42660       [
42661         "Laos (ລາວ)",
42662         "la",
42663         "856"
42664       ],
42665       [
42666         "Latvia (Latvija)",
42667         "lv",
42668         "371"
42669       ],
42670       [
42671         "Lebanon (‫لبنان‬‎)",
42672         "lb",
42673         "961"
42674       ],
42675       [
42676         "Lesotho",
42677         "ls",
42678         "266"
42679       ],
42680       [
42681         "Liberia",
42682         "lr",
42683         "231"
42684       ],
42685       [
42686         "Libya (‫ليبيا‬‎)",
42687         "ly",
42688         "218"
42689       ],
42690       [
42691         "Liechtenstein",
42692         "li",
42693         "423"
42694       ],
42695       [
42696         "Lithuania (Lietuva)",
42697         "lt",
42698         "370"
42699       ],
42700       [
42701         "Luxembourg",
42702         "lu",
42703         "352"
42704       ],
42705       [
42706         "Macau (澳門)",
42707         "mo",
42708         "853"
42709       ],
42710       [
42711         "Macedonia (FYROM) (Македонија)",
42712         "mk",
42713         "389"
42714       ],
42715       [
42716         "Madagascar (Madagasikara)",
42717         "mg",
42718         "261"
42719       ],
42720       [
42721         "Malawi",
42722         "mw",
42723         "265"
42724       ],
42725       [
42726         "Malaysia",
42727         "my",
42728         "60"
42729       ],
42730       [
42731         "Maldives",
42732         "mv",
42733         "960"
42734       ],
42735       [
42736         "Mali",
42737         "ml",
42738         "223"
42739       ],
42740       [
42741         "Malta",
42742         "mt",
42743         "356"
42744       ],
42745       [
42746         "Marshall Islands",
42747         "mh",
42748         "692"
42749       ],
42750       [
42751         "Martinique",
42752         "mq",
42753         "596"
42754       ],
42755       [
42756         "Mauritania (‫موريتانيا‬‎)",
42757         "mr",
42758         "222"
42759       ],
42760       [
42761         "Mauritius (Moris)",
42762         "mu",
42763         "230"
42764       ],
42765       [
42766         "Mayotte",
42767         "yt",
42768         "262",
42769         1
42770       ],
42771       [
42772         "Mexico (México)",
42773         "mx",
42774         "52"
42775       ],
42776       [
42777         "Micronesia",
42778         "fm",
42779         "691"
42780       ],
42781       [
42782         "Moldova (Republica Moldova)",
42783         "md",
42784         "373"
42785       ],
42786       [
42787         "Monaco",
42788         "mc",
42789         "377"
42790       ],
42791       [
42792         "Mongolia (Монгол)",
42793         "mn",
42794         "976"
42795       ],
42796       [
42797         "Montenegro (Crna Gora)",
42798         "me",
42799         "382"
42800       ],
42801       [
42802         "Montserrat",
42803         "ms",
42804         "1664"
42805       ],
42806       [
42807         "Morocco (‫المغرب‬‎)",
42808         "ma",
42809         "212",
42810         0
42811       ],
42812       [
42813         "Mozambique (Moçambique)",
42814         "mz",
42815         "258"
42816       ],
42817       [
42818         "Myanmar (Burma) (မြန်မာ)",
42819         "mm",
42820         "95"
42821       ],
42822       [
42823         "Namibia (Namibië)",
42824         "na",
42825         "264"
42826       ],
42827       [
42828         "Nauru",
42829         "nr",
42830         "674"
42831       ],
42832       [
42833         "Nepal (नेपाल)",
42834         "np",
42835         "977"
42836       ],
42837       [
42838         "Netherlands (Nederland)",
42839         "nl",
42840         "31"
42841       ],
42842       [
42843         "New Caledonia (Nouvelle-Calédonie)",
42844         "nc",
42845         "687"
42846       ],
42847       [
42848         "New Zealand",
42849         "nz",
42850         "64"
42851       ],
42852       [
42853         "Nicaragua",
42854         "ni",
42855         "505"
42856       ],
42857       [
42858         "Niger (Nijar)",
42859         "ne",
42860         "227"
42861       ],
42862       [
42863         "Nigeria",
42864         "ng",
42865         "234"
42866       ],
42867       [
42868         "Niue",
42869         "nu",
42870         "683"
42871       ],
42872       [
42873         "Norfolk Island",
42874         "nf",
42875         "672"
42876       ],
42877       [
42878         "North Korea (조선 민주주의 인민 공화국)",
42879         "kp",
42880         "850"
42881       ],
42882       [
42883         "Northern Mariana Islands",
42884         "mp",
42885         "1670"
42886       ],
42887       [
42888         "Norway (Norge)",
42889         "no",
42890         "47",
42891         0
42892       ],
42893       [
42894         "Oman (‫عُمان‬‎)",
42895         "om",
42896         "968"
42897       ],
42898       [
42899         "Pakistan (‫پاکستان‬‎)",
42900         "pk",
42901         "92"
42902       ],
42903       [
42904         "Palau",
42905         "pw",
42906         "680"
42907       ],
42908       [
42909         "Palestine (‫فلسطين‬‎)",
42910         "ps",
42911         "970"
42912       ],
42913       [
42914         "Panama (Panamá)",
42915         "pa",
42916         "507"
42917       ],
42918       [
42919         "Papua New Guinea",
42920         "pg",
42921         "675"
42922       ],
42923       [
42924         "Paraguay",
42925         "py",
42926         "595"
42927       ],
42928       [
42929         "Peru (Perú)",
42930         "pe",
42931         "51"
42932       ],
42933       [
42934         "Philippines",
42935         "ph",
42936         "63"
42937       ],
42938       [
42939         "Poland (Polska)",
42940         "pl",
42941         "48"
42942       ],
42943       [
42944         "Portugal",
42945         "pt",
42946         "351"
42947       ],
42948       [
42949         "Puerto Rico",
42950         "pr",
42951         "1",
42952         3,
42953         ["787", "939"]
42954       ],
42955       [
42956         "Qatar (‫قطر‬‎)",
42957         "qa",
42958         "974"
42959       ],
42960       [
42961         "Réunion (La Réunion)",
42962         "re",
42963         "262",
42964         0
42965       ],
42966       [
42967         "Romania (România)",
42968         "ro",
42969         "40"
42970       ],
42971       [
42972         "Russia (Россия)",
42973         "ru",
42974         "7",
42975         0
42976       ],
42977       [
42978         "Rwanda",
42979         "rw",
42980         "250"
42981       ],
42982       [
42983         "Saint Barthélemy",
42984         "bl",
42985         "590",
42986         1
42987       ],
42988       [
42989         "Saint Helena",
42990         "sh",
42991         "290"
42992       ],
42993       [
42994         "Saint Kitts and Nevis",
42995         "kn",
42996         "1869"
42997       ],
42998       [
42999         "Saint Lucia",
43000         "lc",
43001         "1758"
43002       ],
43003       [
43004         "Saint Martin (Saint-Martin (partie française))",
43005         "mf",
43006         "590",
43007         2
43008       ],
43009       [
43010         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43011         "pm",
43012         "508"
43013       ],
43014       [
43015         "Saint Vincent and the Grenadines",
43016         "vc",
43017         "1784"
43018       ],
43019       [
43020         "Samoa",
43021         "ws",
43022         "685"
43023       ],
43024       [
43025         "San Marino",
43026         "sm",
43027         "378"
43028       ],
43029       [
43030         "São Tomé and Príncipe (São Tomé e Príncipe)",
43031         "st",
43032         "239"
43033       ],
43034       [
43035         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43036         "sa",
43037         "966"
43038       ],
43039       [
43040         "Senegal (Sénégal)",
43041         "sn",
43042         "221"
43043       ],
43044       [
43045         "Serbia (Србија)",
43046         "rs",
43047         "381"
43048       ],
43049       [
43050         "Seychelles",
43051         "sc",
43052         "248"
43053       ],
43054       [
43055         "Sierra Leone",
43056         "sl",
43057         "232"
43058       ],
43059       [
43060         "Singapore",
43061         "sg",
43062         "65"
43063       ],
43064       [
43065         "Sint Maarten",
43066         "sx",
43067         "1721"
43068       ],
43069       [
43070         "Slovakia (Slovensko)",
43071         "sk",
43072         "421"
43073       ],
43074       [
43075         "Slovenia (Slovenija)",
43076         "si",
43077         "386"
43078       ],
43079       [
43080         "Solomon Islands",
43081         "sb",
43082         "677"
43083       ],
43084       [
43085         "Somalia (Soomaaliya)",
43086         "so",
43087         "252"
43088       ],
43089       [
43090         "South Africa",
43091         "za",
43092         "27"
43093       ],
43094       [
43095         "South Korea (대한민국)",
43096         "kr",
43097         "82"
43098       ],
43099       [
43100         "South Sudan (‫جنوب السودان‬‎)",
43101         "ss",
43102         "211"
43103       ],
43104       [
43105         "Spain (España)",
43106         "es",
43107         "34"
43108       ],
43109       [
43110         "Sri Lanka (ශ්‍රී ලංකාව)",
43111         "lk",
43112         "94"
43113       ],
43114       [
43115         "Sudan (‫السودان‬‎)",
43116         "sd",
43117         "249"
43118       ],
43119       [
43120         "Suriname",
43121         "sr",
43122         "597"
43123       ],
43124       [
43125         "Svalbard and Jan Mayen",
43126         "sj",
43127         "47",
43128         1
43129       ],
43130       [
43131         "Swaziland",
43132         "sz",
43133         "268"
43134       ],
43135       [
43136         "Sweden (Sverige)",
43137         "se",
43138         "46"
43139       ],
43140       [
43141         "Switzerland (Schweiz)",
43142         "ch",
43143         "41"
43144       ],
43145       [
43146         "Syria (‫سوريا‬‎)",
43147         "sy",
43148         "963"
43149       ],
43150       [
43151         "Taiwan (台灣)",
43152         "tw",
43153         "886"
43154       ],
43155       [
43156         "Tajikistan",
43157         "tj",
43158         "992"
43159       ],
43160       [
43161         "Tanzania",
43162         "tz",
43163         "255"
43164       ],
43165       [
43166         "Thailand (ไทย)",
43167         "th",
43168         "66"
43169       ],
43170       [
43171         "Timor-Leste",
43172         "tl",
43173         "670"
43174       ],
43175       [
43176         "Togo",
43177         "tg",
43178         "228"
43179       ],
43180       [
43181         "Tokelau",
43182         "tk",
43183         "690"
43184       ],
43185       [
43186         "Tonga",
43187         "to",
43188         "676"
43189       ],
43190       [
43191         "Trinidad and Tobago",
43192         "tt",
43193         "1868"
43194       ],
43195       [
43196         "Tunisia (‫تونس‬‎)",
43197         "tn",
43198         "216"
43199       ],
43200       [
43201         "Turkey (Türkiye)",
43202         "tr",
43203         "90"
43204       ],
43205       [
43206         "Turkmenistan",
43207         "tm",
43208         "993"
43209       ],
43210       [
43211         "Turks and Caicos Islands",
43212         "tc",
43213         "1649"
43214       ],
43215       [
43216         "Tuvalu",
43217         "tv",
43218         "688"
43219       ],
43220       [
43221         "U.S. Virgin Islands",
43222         "vi",
43223         "1340"
43224       ],
43225       [
43226         "Uganda",
43227         "ug",
43228         "256"
43229       ],
43230       [
43231         "Ukraine (Україна)",
43232         "ua",
43233         "380"
43234       ],
43235       [
43236         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43237         "ae",
43238         "971"
43239       ],
43240       [
43241         "United Kingdom",
43242         "gb",
43243         "44",
43244         0
43245       ],
43246       [
43247         "United States",
43248         "us",
43249         "1",
43250         0
43251       ],
43252       [
43253         "Uruguay",
43254         "uy",
43255         "598"
43256       ],
43257       [
43258         "Uzbekistan (Oʻzbekiston)",
43259         "uz",
43260         "998"
43261       ],
43262       [
43263         "Vanuatu",
43264         "vu",
43265         "678"
43266       ],
43267       [
43268         "Vatican City (Città del Vaticano)",
43269         "va",
43270         "39",
43271         1
43272       ],
43273       [
43274         "Venezuela",
43275         "ve",
43276         "58"
43277       ],
43278       [
43279         "Vietnam (Việt Nam)",
43280         "vn",
43281         "84"
43282       ],
43283       [
43284         "Wallis and Futuna (Wallis-et-Futuna)",
43285         "wf",
43286         "681"
43287       ],
43288       [
43289         "Western Sahara (‫الصحراء الغربية‬‎)",
43290         "eh",
43291         "212",
43292         1
43293       ],
43294       [
43295         "Yemen (‫اليمن‬‎)",
43296         "ye",
43297         "967"
43298       ],
43299       [
43300         "Zambia",
43301         "zm",
43302         "260"
43303       ],
43304       [
43305         "Zimbabwe",
43306         "zw",
43307         "263"
43308       ],
43309       [
43310         "Åland Islands",
43311         "ax",
43312         "358",
43313         1
43314       ]
43315   ];
43316   
43317   return d;
43318 }/**
43319 *    This script refer to:
43320 *    Title: International Telephone Input
43321 *    Author: Jack O'Connor
43322 *    Code version:  v12.1.12
43323 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43324 **/
43325
43326 /**
43327  * @class Roo.bootstrap.form.PhoneInput
43328  * @extends Roo.bootstrap.form.TriggerField
43329  * An input with International dial-code selection
43330  
43331  * @cfg {String} defaultDialCode default '+852'
43332  * @cfg {Array} preferedCountries default []
43333   
43334  * @constructor
43335  * Create a new PhoneInput.
43336  * @param {Object} config Configuration options
43337  */
43338
43339 Roo.bootstrap.form.PhoneInput = function(config) {
43340     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43341 };
43342
43343 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43344         /**
43345         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43346         */
43347         listWidth: undefined,
43348         
43349         selectedClass: 'active',
43350         
43351         invalidClass : "has-warning",
43352         
43353         validClass: 'has-success',
43354         
43355         allowed: '0123456789',
43356         
43357         max_length: 15,
43358         
43359         /**
43360          * @cfg {String} defaultDialCode The default dial code when initializing the input
43361          */
43362         defaultDialCode: '+852',
43363         
43364         /**
43365          * @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
43366          */
43367         preferedCountries: false,
43368         
43369         getAutoCreate : function()
43370         {
43371             var data = Roo.bootstrap.form.PhoneInputData();
43372             var align = this.labelAlign || this.parentLabelAlign();
43373             var id = Roo.id();
43374             
43375             this.allCountries = [];
43376             this.dialCodeMapping = [];
43377             
43378             for (var i = 0; i < data.length; i++) {
43379               var c = data[i];
43380               this.allCountries[i] = {
43381                 name: c[0],
43382                 iso2: c[1],
43383                 dialCode: c[2],
43384                 priority: c[3] || 0,
43385                 areaCodes: c[4] || null
43386               };
43387               this.dialCodeMapping[c[2]] = {
43388                   name: c[0],
43389                   iso2: c[1],
43390                   priority: c[3] || 0,
43391                   areaCodes: c[4] || null
43392               };
43393             }
43394             
43395             var cfg = {
43396                 cls: 'form-group',
43397                 cn: []
43398             };
43399             
43400             var input =  {
43401                 tag: 'input',
43402                 id : id,
43403                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43404                 maxlength: this.max_length,
43405                 cls : 'form-control tel-input',
43406                 autocomplete: 'new-password'
43407             };
43408             
43409             var hiddenInput = {
43410                 tag: 'input',
43411                 type: 'hidden',
43412                 cls: 'hidden-tel-input'
43413             };
43414             
43415             if (this.name) {
43416                 hiddenInput.name = this.name;
43417             }
43418             
43419             if (this.disabled) {
43420                 input.disabled = true;
43421             }
43422             
43423             var flag_container = {
43424                 tag: 'div',
43425                 cls: 'flag-box',
43426                 cn: [
43427                     {
43428                         tag: 'div',
43429                         cls: 'flag'
43430                     },
43431                     {
43432                         tag: 'div',
43433                         cls: 'caret'
43434                     }
43435                 ]
43436             };
43437             
43438             var box = {
43439                 tag: 'div',
43440                 cls: this.hasFeedback ? 'has-feedback' : '',
43441                 cn: [
43442                     hiddenInput,
43443                     input,
43444                     {
43445                         tag: 'input',
43446                         cls: 'dial-code-holder',
43447                         disabled: true
43448                     }
43449                 ]
43450             };
43451             
43452             var container = {
43453                 cls: 'roo-select2-container input-group',
43454                 cn: [
43455                     flag_container,
43456                     box
43457                 ]
43458             };
43459             
43460             if (this.fieldLabel.length) {
43461                 var indicator = {
43462                     tag: 'i',
43463                     tooltip: 'This field is required'
43464                 };
43465                 
43466                 var label = {
43467                     tag: 'label',
43468                     'for':  id,
43469                     cls: 'control-label',
43470                     cn: []
43471                 };
43472                 
43473                 var label_text = {
43474                     tag: 'span',
43475                     html: this.fieldLabel
43476                 };
43477                 
43478                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43479                 label.cn = [
43480                     indicator,
43481                     label_text
43482                 ];
43483                 
43484                 if(this.indicatorpos == 'right') {
43485                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43486                     label.cn = [
43487                         label_text,
43488                         indicator
43489                     ];
43490                 }
43491                 
43492                 if(align == 'left') {
43493                     container = {
43494                         tag: 'div',
43495                         cn: [
43496                             container
43497                         ]
43498                     };
43499                     
43500                     if(this.labelWidth > 12){
43501                         label.style = "width: " + this.labelWidth + 'px';
43502                     }
43503                     if(this.labelWidth < 13 && this.labelmd == 0){
43504                         this.labelmd = this.labelWidth;
43505                     }
43506                     if(this.labellg > 0){
43507                         label.cls += ' col-lg-' + this.labellg;
43508                         input.cls += ' col-lg-' + (12 - this.labellg);
43509                     }
43510                     if(this.labelmd > 0){
43511                         label.cls += ' col-md-' + this.labelmd;
43512                         container.cls += ' col-md-' + (12 - this.labelmd);
43513                     }
43514                     if(this.labelsm > 0){
43515                         label.cls += ' col-sm-' + this.labelsm;
43516                         container.cls += ' col-sm-' + (12 - this.labelsm);
43517                     }
43518                     if(this.labelxs > 0){
43519                         label.cls += ' col-xs-' + this.labelxs;
43520                         container.cls += ' col-xs-' + (12 - this.labelxs);
43521                     }
43522                 }
43523             }
43524             
43525             cfg.cn = [
43526                 label,
43527                 container
43528             ];
43529             
43530             var settings = this;
43531             
43532             ['xs','sm','md','lg'].map(function(size){
43533                 if (settings[size]) {
43534                     cfg.cls += ' col-' + size + '-' + settings[size];
43535                 }
43536             });
43537             
43538             this.store = new Roo.data.Store({
43539                 proxy : new Roo.data.MemoryProxy({}),
43540                 reader : new Roo.data.JsonReader({
43541                     fields : [
43542                         {
43543                             'name' : 'name',
43544                             'type' : 'string'
43545                         },
43546                         {
43547                             'name' : 'iso2',
43548                             'type' : 'string'
43549                         },
43550                         {
43551                             'name' : 'dialCode',
43552                             'type' : 'string'
43553                         },
43554                         {
43555                             'name' : 'priority',
43556                             'type' : 'string'
43557                         },
43558                         {
43559                             'name' : 'areaCodes',
43560                             'type' : 'string'
43561                         }
43562                     ]
43563                 })
43564             });
43565             
43566             if(!this.preferedCountries) {
43567                 this.preferedCountries = [
43568                     'hk',
43569                     'gb',
43570                     'us'
43571                 ];
43572             }
43573             
43574             var p = this.preferedCountries.reverse();
43575             
43576             if(p) {
43577                 for (var i = 0; i < p.length; i++) {
43578                     for (var j = 0; j < this.allCountries.length; j++) {
43579                         if(this.allCountries[j].iso2 == p[i]) {
43580                             var t = this.allCountries[j];
43581                             this.allCountries.splice(j,1);
43582                             this.allCountries.unshift(t);
43583                         }
43584                     } 
43585                 }
43586             }
43587             
43588             this.store.proxy.data = {
43589                 success: true,
43590                 data: this.allCountries
43591             };
43592             
43593             return cfg;
43594         },
43595         
43596         initEvents : function()
43597         {
43598             this.createList();
43599             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43600             
43601             this.indicator = this.indicatorEl();
43602             this.flag = this.flagEl();
43603             this.dialCodeHolder = this.dialCodeHolderEl();
43604             
43605             this.trigger = this.el.select('div.flag-box',true).first();
43606             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43607             
43608             var _this = this;
43609             
43610             (function(){
43611                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43612                 _this.list.setWidth(lw);
43613             }).defer(100);
43614             
43615             this.list.on('mouseover', this.onViewOver, this);
43616             this.list.on('mousemove', this.onViewMove, this);
43617             this.inputEl().on("keyup", this.onKeyUp, this);
43618             this.inputEl().on("keypress", this.onKeyPress, this);
43619             
43620             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43621
43622             this.view = new Roo.View(this.list, this.tpl, {
43623                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43624             });
43625             
43626             this.view.on('click', this.onViewClick, this);
43627             this.setValue(this.defaultDialCode);
43628         },
43629         
43630         onTriggerClick : function(e)
43631         {
43632             Roo.log('trigger click');
43633             if(this.disabled){
43634                 return;
43635             }
43636             
43637             if(this.isExpanded()){
43638                 this.collapse();
43639                 this.hasFocus = false;
43640             }else {
43641                 this.store.load({});
43642                 this.hasFocus = true;
43643                 this.expand();
43644             }
43645         },
43646         
43647         isExpanded : function()
43648         {
43649             return this.list.isVisible();
43650         },
43651         
43652         collapse : function()
43653         {
43654             if(!this.isExpanded()){
43655                 return;
43656             }
43657             this.list.hide();
43658             Roo.get(document).un('mousedown', this.collapseIf, this);
43659             Roo.get(document).un('mousewheel', this.collapseIf, this);
43660             this.fireEvent('collapse', this);
43661             this.validate();
43662         },
43663         
43664         expand : function()
43665         {
43666             Roo.log('expand');
43667
43668             if(this.isExpanded() || !this.hasFocus){
43669                 return;
43670             }
43671             
43672             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43673             this.list.setWidth(lw);
43674             
43675             this.list.show();
43676             this.restrictHeight();
43677             
43678             Roo.get(document).on('mousedown', this.collapseIf, this);
43679             Roo.get(document).on('mousewheel', this.collapseIf, this);
43680             
43681             this.fireEvent('expand', this);
43682         },
43683         
43684         restrictHeight : function()
43685         {
43686             this.list.alignTo(this.inputEl(), this.listAlign);
43687             this.list.alignTo(this.inputEl(), this.listAlign);
43688         },
43689         
43690         onViewOver : function(e, t)
43691         {
43692             if(this.inKeyMode){
43693                 return;
43694             }
43695             var item = this.view.findItemFromChild(t);
43696             
43697             if(item){
43698                 var index = this.view.indexOf(item);
43699                 this.select(index, false);
43700             }
43701         },
43702
43703         // private
43704         onViewClick : function(view, doFocus, el, e)
43705         {
43706             var index = this.view.getSelectedIndexes()[0];
43707             
43708             var r = this.store.getAt(index);
43709             
43710             if(r){
43711                 this.onSelect(r, index);
43712             }
43713             if(doFocus !== false && !this.blockFocus){
43714                 this.inputEl().focus();
43715             }
43716         },
43717         
43718         onViewMove : function(e, t)
43719         {
43720             this.inKeyMode = false;
43721         },
43722         
43723         select : function(index, scrollIntoView)
43724         {
43725             this.selectedIndex = index;
43726             this.view.select(index);
43727             if(scrollIntoView !== false){
43728                 var el = this.view.getNode(index);
43729                 if(el){
43730                     this.list.scrollChildIntoView(el, false);
43731                 }
43732             }
43733         },
43734         
43735         createList : function()
43736         {
43737             this.list = Roo.get(document.body).createChild({
43738                 tag: 'ul',
43739                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43740                 style: 'display:none'
43741             });
43742             
43743             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43744         },
43745         
43746         collapseIf : function(e)
43747         {
43748             var in_combo  = e.within(this.el);
43749             var in_list =  e.within(this.list);
43750             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43751             
43752             if (in_combo || in_list || is_list) {
43753                 return;
43754             }
43755             this.collapse();
43756         },
43757         
43758         onSelect : function(record, index)
43759         {
43760             if(this.fireEvent('beforeselect', this, record, index) !== false){
43761                 
43762                 this.setFlagClass(record.data.iso2);
43763                 this.setDialCode(record.data.dialCode);
43764                 this.hasFocus = false;
43765                 this.collapse();
43766                 this.fireEvent('select', this, record, index);
43767             }
43768         },
43769         
43770         flagEl : function()
43771         {
43772             var flag = this.el.select('div.flag',true).first();
43773             if(!flag){
43774                 return false;
43775             }
43776             return flag;
43777         },
43778         
43779         dialCodeHolderEl : function()
43780         {
43781             var d = this.el.select('input.dial-code-holder',true).first();
43782             if(!d){
43783                 return false;
43784             }
43785             return d;
43786         },
43787         
43788         setDialCode : function(v)
43789         {
43790             this.dialCodeHolder.dom.value = '+'+v;
43791         },
43792         
43793         setFlagClass : function(n)
43794         {
43795             this.flag.dom.className = 'flag '+n;
43796         },
43797         
43798         getValue : function()
43799         {
43800             var v = this.inputEl().getValue();
43801             if(this.dialCodeHolder) {
43802                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43803             }
43804             return v;
43805         },
43806         
43807         setValue : function(v)
43808         {
43809             var d = this.getDialCode(v);
43810             
43811             //invalid dial code
43812             if(v.length == 0 || !d || d.length == 0) {
43813                 if(this.rendered){
43814                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43815                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43816                 }
43817                 return;
43818             }
43819             
43820             //valid dial code
43821             this.setFlagClass(this.dialCodeMapping[d].iso2);
43822             this.setDialCode(d);
43823             this.inputEl().dom.value = v.replace('+'+d,'');
43824             this.hiddenEl().dom.value = this.getValue();
43825             
43826             this.validate();
43827         },
43828         
43829         getDialCode : function(v)
43830         {
43831             v = v ||  '';
43832             
43833             if (v.length == 0) {
43834                 return this.dialCodeHolder.dom.value;
43835             }
43836             
43837             var dialCode = "";
43838             if (v.charAt(0) != "+") {
43839                 return false;
43840             }
43841             var numericChars = "";
43842             for (var i = 1; i < v.length; i++) {
43843               var c = v.charAt(i);
43844               if (!isNaN(c)) {
43845                 numericChars += c;
43846                 if (this.dialCodeMapping[numericChars]) {
43847                   dialCode = v.substr(1, i);
43848                 }
43849                 if (numericChars.length == 4) {
43850                   break;
43851                 }
43852               }
43853             }
43854             return dialCode;
43855         },
43856         
43857         reset : function()
43858         {
43859             this.setValue(this.defaultDialCode);
43860             this.validate();
43861         },
43862         
43863         hiddenEl : function()
43864         {
43865             return this.el.select('input.hidden-tel-input',true).first();
43866         },
43867         
43868         // after setting val
43869         onKeyUp : function(e){
43870             this.setValue(this.getValue());
43871         },
43872         
43873         onKeyPress : function(e){
43874             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43875                 e.stopEvent();
43876             }
43877         }
43878         
43879 });
43880 /**
43881  * @class Roo.bootstrap.form.MoneyField
43882  * @extends Roo.bootstrap.form.ComboBox
43883  * Bootstrap MoneyField class
43884  * 
43885  * @constructor
43886  * Create a new MoneyField.
43887  * @param {Object} config Configuration options
43888  */
43889
43890 Roo.bootstrap.form.MoneyField = function(config) {
43891     
43892     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43893     
43894 };
43895
43896 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43897     
43898     /**
43899      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43900      */
43901     allowDecimals : true,
43902     /**
43903      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43904      */
43905     decimalSeparator : ".",
43906     /**
43907      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43908      */
43909     decimalPrecision : 0,
43910     /**
43911      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43912      */
43913     allowNegative : true,
43914     /**
43915      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43916      */
43917     allowZero: true,
43918     /**
43919      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43920      */
43921     minValue : Number.NEGATIVE_INFINITY,
43922     /**
43923      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43924      */
43925     maxValue : Number.MAX_VALUE,
43926     /**
43927      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43928      */
43929     minText : "The minimum value for this field is {0}",
43930     /**
43931      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43932      */
43933     maxText : "The maximum value for this field is {0}",
43934     /**
43935      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43936      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43937      */
43938     nanText : "{0} is not a valid number",
43939     /**
43940      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43941      */
43942     castInt : true,
43943     /**
43944      * @cfg {String} defaults currency of the MoneyField
43945      * value should be in lkey
43946      */
43947     defaultCurrency : false,
43948     /**
43949      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43950      */
43951     thousandsDelimiter : false,
43952     /**
43953      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43954      */
43955     max_length: false,
43956     
43957     inputlg : 9,
43958     inputmd : 9,
43959     inputsm : 9,
43960     inputxs : 6,
43961      /**
43962      * @cfg {Roo.data.Store} store  Store to lookup currency??
43963      */
43964     store : false,
43965     
43966     getAutoCreate : function()
43967     {
43968         var align = this.labelAlign || this.parentLabelAlign();
43969         
43970         var id = Roo.id();
43971
43972         var cfg = {
43973             cls: 'form-group',
43974             cn: []
43975         };
43976
43977         var input =  {
43978             tag: 'input',
43979             id : id,
43980             cls : 'form-control roo-money-amount-input',
43981             autocomplete: 'new-password'
43982         };
43983         
43984         var hiddenInput = {
43985             tag: 'input',
43986             type: 'hidden',
43987             id: Roo.id(),
43988             cls: 'hidden-number-input'
43989         };
43990         
43991         if(this.max_length) {
43992             input.maxlength = this.max_length; 
43993         }
43994         
43995         if (this.name) {
43996             hiddenInput.name = this.name;
43997         }
43998
43999         if (this.disabled) {
44000             input.disabled = true;
44001         }
44002
44003         var clg = 12 - this.inputlg;
44004         var cmd = 12 - this.inputmd;
44005         var csm = 12 - this.inputsm;
44006         var cxs = 12 - this.inputxs;
44007         
44008         var container = {
44009             tag : 'div',
44010             cls : 'row roo-money-field',
44011             cn : [
44012                 {
44013                     tag : 'div',
44014                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44015                     cn : [
44016                         {
44017                             tag : 'div',
44018                             cls: 'roo-select2-container input-group',
44019                             cn: [
44020                                 {
44021                                     tag : 'input',
44022                                     cls : 'form-control roo-money-currency-input',
44023                                     autocomplete: 'new-password',
44024                                     readOnly : 1,
44025                                     name : this.currencyName
44026                                 },
44027                                 {
44028                                     tag :'span',
44029                                     cls : 'input-group-addon',
44030                                     cn : [
44031                                         {
44032                                             tag: 'span',
44033                                             cls: 'caret'
44034                                         }
44035                                     ]
44036                                 }
44037                             ]
44038                         }
44039                     ]
44040                 },
44041                 {
44042                     tag : 'div',
44043                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44044                     cn : [
44045                         {
44046                             tag: 'div',
44047                             cls: this.hasFeedback ? 'has-feedback' : '',
44048                             cn: [
44049                                 input
44050                             ]
44051                         }
44052                     ]
44053                 }
44054             ]
44055             
44056         };
44057         
44058         if (this.fieldLabel.length) {
44059             var indicator = {
44060                 tag: 'i',
44061                 tooltip: 'This field is required'
44062             };
44063
44064             var label = {
44065                 tag: 'label',
44066                 'for':  id,
44067                 cls: 'control-label',
44068                 cn: []
44069             };
44070
44071             var label_text = {
44072                 tag: 'span',
44073                 html: this.fieldLabel
44074             };
44075
44076             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44077             label.cn = [
44078                 indicator,
44079                 label_text
44080             ];
44081
44082             if(this.indicatorpos == 'right') {
44083                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44084                 label.cn = [
44085                     label_text,
44086                     indicator
44087                 ];
44088             }
44089
44090             if(align == 'left') {
44091                 container = {
44092                     tag: 'div',
44093                     cn: [
44094                         container
44095                     ]
44096                 };
44097
44098                 if(this.labelWidth > 12){
44099                     label.style = "width: " + this.labelWidth + 'px';
44100                 }
44101                 if(this.labelWidth < 13 && this.labelmd == 0){
44102                     this.labelmd = this.labelWidth;
44103                 }
44104                 if(this.labellg > 0){
44105                     label.cls += ' col-lg-' + this.labellg;
44106                     input.cls += ' col-lg-' + (12 - this.labellg);
44107                 }
44108                 if(this.labelmd > 0){
44109                     label.cls += ' col-md-' + this.labelmd;
44110                     container.cls += ' col-md-' + (12 - this.labelmd);
44111                 }
44112                 if(this.labelsm > 0){
44113                     label.cls += ' col-sm-' + this.labelsm;
44114                     container.cls += ' col-sm-' + (12 - this.labelsm);
44115                 }
44116                 if(this.labelxs > 0){
44117                     label.cls += ' col-xs-' + this.labelxs;
44118                     container.cls += ' col-xs-' + (12 - this.labelxs);
44119                 }
44120             }
44121         }
44122
44123         cfg.cn = [
44124             label,
44125             container,
44126             hiddenInput
44127         ];
44128         
44129         var settings = this;
44130
44131         ['xs','sm','md','lg'].map(function(size){
44132             if (settings[size]) {
44133                 cfg.cls += ' col-' + size + '-' + settings[size];
44134             }
44135         });
44136         
44137         return cfg;
44138     },
44139     
44140     initEvents : function()
44141     {
44142         this.indicator = this.indicatorEl();
44143         
44144         this.initCurrencyEvent();
44145         
44146         this.initNumberEvent();
44147     },
44148     
44149     initCurrencyEvent : function()
44150     {
44151         if (!this.store) {
44152             throw "can not find store for combo";
44153         }
44154         
44155         this.store = Roo.factory(this.store, Roo.data);
44156         this.store.parent = this;
44157         
44158         this.createList();
44159         
44160         this.triggerEl = this.el.select('.input-group-addon', true).first();
44161         
44162         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44163         
44164         var _this = this;
44165         
44166         (function(){
44167             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44168             _this.list.setWidth(lw);
44169         }).defer(100);
44170         
44171         this.list.on('mouseover', this.onViewOver, this);
44172         this.list.on('mousemove', this.onViewMove, this);
44173         this.list.on('scroll', this.onViewScroll, this);
44174         
44175         if(!this.tpl){
44176             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44177         }
44178         
44179         this.view = new Roo.View(this.list, this.tpl, {
44180             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44181         });
44182         
44183         this.view.on('click', this.onViewClick, this);
44184         
44185         this.store.on('beforeload', this.onBeforeLoad, this);
44186         this.store.on('load', this.onLoad, this);
44187         this.store.on('loadexception', this.onLoadException, this);
44188         
44189         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44190             "up" : function(e){
44191                 this.inKeyMode = true;
44192                 this.selectPrev();
44193             },
44194
44195             "down" : function(e){
44196                 if(!this.isExpanded()){
44197                     this.onTriggerClick();
44198                 }else{
44199                     this.inKeyMode = true;
44200                     this.selectNext();
44201                 }
44202             },
44203
44204             "enter" : function(e){
44205                 this.collapse();
44206                 
44207                 if(this.fireEvent("specialkey", this, e)){
44208                     this.onViewClick(false);
44209                 }
44210                 
44211                 return true;
44212             },
44213
44214             "esc" : function(e){
44215                 this.collapse();
44216             },
44217
44218             "tab" : function(e){
44219                 this.collapse();
44220                 
44221                 if(this.fireEvent("specialkey", this, e)){
44222                     this.onViewClick(false);
44223                 }
44224                 
44225                 return true;
44226             },
44227
44228             scope : this,
44229
44230             doRelay : function(foo, bar, hname){
44231                 if(hname == 'down' || this.scope.isExpanded()){
44232                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44233                 }
44234                 return true;
44235             },
44236
44237             forceKeyDown: true
44238         });
44239         
44240         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44241         
44242     },
44243     
44244     initNumberEvent : function(e)
44245     {
44246         this.inputEl().on("keydown" , this.fireKey,  this);
44247         this.inputEl().on("focus", this.onFocus,  this);
44248         this.inputEl().on("blur", this.onBlur,  this);
44249         
44250         this.inputEl().relayEvent('keyup', this);
44251         
44252         if(this.indicator){
44253             this.indicator.addClass('invisible');
44254         }
44255  
44256         this.originalValue = this.getValue();
44257         
44258         if(this.validationEvent == 'keyup'){
44259             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44260             this.inputEl().on('keyup', this.filterValidation, this);
44261         }
44262         else if(this.validationEvent !== false){
44263             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44264         }
44265         
44266         if(this.selectOnFocus){
44267             this.on("focus", this.preFocus, this);
44268             
44269         }
44270         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44271             this.inputEl().on("keypress", this.filterKeys, this);
44272         } else {
44273             this.inputEl().relayEvent('keypress', this);
44274         }
44275         
44276         var allowed = "0123456789";
44277         
44278         if(this.allowDecimals){
44279             allowed += this.decimalSeparator;
44280         }
44281         
44282         if(this.allowNegative){
44283             allowed += "-";
44284         }
44285         
44286         if(this.thousandsDelimiter) {
44287             allowed += ",";
44288         }
44289         
44290         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44291         
44292         var keyPress = function(e){
44293             
44294             var k = e.getKey();
44295             
44296             var c = e.getCharCode();
44297             
44298             if(
44299                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44300                     allowed.indexOf(String.fromCharCode(c)) === -1
44301             ){
44302                 e.stopEvent();
44303                 return;
44304             }
44305             
44306             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44307                 return;
44308             }
44309             
44310             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44311                 e.stopEvent();
44312             }
44313         };
44314         
44315         this.inputEl().on("keypress", keyPress, this);
44316         
44317     },
44318     
44319     onTriggerClick : function(e)
44320     {   
44321         if(this.disabled){
44322             return;
44323         }
44324         
44325         this.page = 0;
44326         this.loadNext = false;
44327         
44328         if(this.isExpanded()){
44329             this.collapse();
44330             return;
44331         }
44332         
44333         this.hasFocus = true;
44334         
44335         if(this.triggerAction == 'all') {
44336             this.doQuery(this.allQuery, true);
44337             return;
44338         }
44339         
44340         this.doQuery(this.getRawValue());
44341     },
44342     
44343     getCurrency : function()
44344     {   
44345         var v = this.currencyEl().getValue();
44346         
44347         return v;
44348     },
44349     
44350     restrictHeight : function()
44351     {
44352         this.list.alignTo(this.currencyEl(), this.listAlign);
44353         this.list.alignTo(this.currencyEl(), this.listAlign);
44354     },
44355     
44356     onViewClick : function(view, doFocus, el, e)
44357     {
44358         var index = this.view.getSelectedIndexes()[0];
44359         
44360         var r = this.store.getAt(index);
44361         
44362         if(r){
44363             this.onSelect(r, index);
44364         }
44365     },
44366     
44367     onSelect : function(record, index){
44368         
44369         if(this.fireEvent('beforeselect', this, record, index) !== false){
44370         
44371             this.setFromCurrencyData(index > -1 ? record.data : false);
44372             
44373             this.collapse();
44374             
44375             this.fireEvent('select', this, record, index);
44376         }
44377     },
44378     
44379     setFromCurrencyData : function(o)
44380     {
44381         var currency = '';
44382         
44383         this.lastCurrency = o;
44384         
44385         if (this.currencyField) {
44386             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44387         } else {
44388             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44389         }
44390         
44391         this.lastSelectionText = currency;
44392         
44393         //setting default currency
44394         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44395             this.setCurrency(this.defaultCurrency);
44396             return;
44397         }
44398         
44399         this.setCurrency(currency);
44400     },
44401     
44402     setFromData : function(o)
44403     {
44404         var c = {};
44405         
44406         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44407         
44408         this.setFromCurrencyData(c);
44409         
44410         var value = '';
44411         
44412         if (this.name) {
44413             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44414         } else {
44415             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44416         }
44417         
44418         this.setValue(value);
44419         
44420     },
44421     
44422     setCurrency : function(v)
44423     {   
44424         this.currencyValue = v;
44425         
44426         if(this.rendered){
44427             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44428             this.validate();
44429         }
44430     },
44431     
44432     setValue : function(v)
44433     {
44434         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44435         
44436         this.value = v;
44437         
44438         if(this.rendered){
44439             
44440             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44441             
44442             this.inputEl().dom.value = (v == '') ? '' :
44443                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44444             
44445             if(!this.allowZero && v === '0') {
44446                 this.hiddenEl().dom.value = '';
44447                 this.inputEl().dom.value = '';
44448             }
44449             
44450             this.validate();
44451         }
44452     },
44453     
44454     getRawValue : function()
44455     {
44456         var v = this.inputEl().getValue();
44457         
44458         return v;
44459     },
44460     
44461     getValue : function()
44462     {
44463         return this.fixPrecision(this.parseValue(this.getRawValue()));
44464     },
44465     
44466     parseValue : function(value)
44467     {
44468         if(this.thousandsDelimiter) {
44469             value += "";
44470             r = new RegExp(",", "g");
44471             value = value.replace(r, "");
44472         }
44473         
44474         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44475         return isNaN(value) ? '' : value;
44476         
44477     },
44478     
44479     fixPrecision : function(value)
44480     {
44481         if(this.thousandsDelimiter) {
44482             value += "";
44483             r = new RegExp(",", "g");
44484             value = value.replace(r, "");
44485         }
44486         
44487         var nan = isNaN(value);
44488         
44489         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44490             return nan ? '' : value;
44491         }
44492         return parseFloat(value).toFixed(this.decimalPrecision);
44493     },
44494     
44495     decimalPrecisionFcn : function(v)
44496     {
44497         return Math.floor(v);
44498     },
44499     
44500     validateValue : function(value)
44501     {
44502         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44503             return false;
44504         }
44505         
44506         var num = this.parseValue(value);
44507         
44508         if(isNaN(num)){
44509             this.markInvalid(String.format(this.nanText, value));
44510             return false;
44511         }
44512         
44513         if(num < this.minValue){
44514             this.markInvalid(String.format(this.minText, this.minValue));
44515             return false;
44516         }
44517         
44518         if(num > this.maxValue){
44519             this.markInvalid(String.format(this.maxText, this.maxValue));
44520             return false;
44521         }
44522         
44523         return true;
44524     },
44525     
44526     validate : function()
44527     {
44528         if(this.disabled || this.allowBlank){
44529             this.markValid();
44530             return true;
44531         }
44532         
44533         var currency = this.getCurrency();
44534         
44535         if(this.validateValue(this.getRawValue()) && currency.length){
44536             this.markValid();
44537             return true;
44538         }
44539         
44540         this.markInvalid();
44541         return false;
44542     },
44543     
44544     getName: function()
44545     {
44546         return this.name;
44547     },
44548     
44549     beforeBlur : function()
44550     {
44551         if(!this.castInt){
44552             return;
44553         }
44554         
44555         var v = this.parseValue(this.getRawValue());
44556         
44557         if(v || v == 0){
44558             this.setValue(v);
44559         }
44560     },
44561     
44562     onBlur : function()
44563     {
44564         this.beforeBlur();
44565         
44566         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44567             //this.el.removeClass(this.focusClass);
44568         }
44569         
44570         this.hasFocus = false;
44571         
44572         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44573             this.validate();
44574         }
44575         
44576         var v = this.getValue();
44577         
44578         if(String(v) !== String(this.startValue)){
44579             this.fireEvent('change', this, v, this.startValue);
44580         }
44581         
44582         this.fireEvent("blur", this);
44583     },
44584     
44585     inputEl : function()
44586     {
44587         return this.el.select('.roo-money-amount-input', true).first();
44588     },
44589     
44590     currencyEl : function()
44591     {
44592         return this.el.select('.roo-money-currency-input', true).first();
44593     },
44594     
44595     hiddenEl : function()
44596     {
44597         return this.el.select('input.hidden-number-input',true).first();
44598     }
44599     
44600 });/**
44601  * @class Roo.bootstrap.BezierSignature
44602  * @extends Roo.bootstrap.Component
44603  * Bootstrap BezierSignature class
44604  * This script refer to:
44605  *    Title: Signature Pad
44606  *    Author: szimek
44607  *    Availability: https://github.com/szimek/signature_pad
44608  *
44609  * @constructor
44610  * Create a new BezierSignature
44611  * @param {Object} config The config object
44612  */
44613
44614 Roo.bootstrap.BezierSignature = function(config){
44615     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44616     this.addEvents({
44617         "resize" : true
44618     });
44619 };
44620
44621 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44622 {
44623      
44624     curve_data: [],
44625     
44626     is_empty: true,
44627     
44628     mouse_btn_down: true,
44629     
44630     /**
44631      * @cfg {int} canvas height
44632      */
44633     canvas_height: '200px',
44634     
44635     /**
44636      * @cfg {float|function} Radius of a single dot.
44637      */ 
44638     dot_size: false,
44639     
44640     /**
44641      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44642      */
44643     min_width: 0.5,
44644     
44645     /**
44646      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44647      */
44648     max_width: 2.5,
44649     
44650     /**
44651      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44652      */
44653     throttle: 16,
44654     
44655     /**
44656      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44657      */
44658     min_distance: 5,
44659     
44660     /**
44661      * @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.
44662      */
44663     bg_color: 'rgba(0, 0, 0, 0)',
44664     
44665     /**
44666      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44667      */
44668     dot_color: 'black',
44669     
44670     /**
44671      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44672      */ 
44673     velocity_filter_weight: 0.7,
44674     
44675     /**
44676      * @cfg {function} Callback when stroke begin. 
44677      */
44678     onBegin: false,
44679     
44680     /**
44681      * @cfg {function} Callback when stroke end.
44682      */
44683     onEnd: false,
44684     
44685     getAutoCreate : function()
44686     {
44687         var cls = 'roo-signature column';
44688         
44689         if(this.cls){
44690             cls += ' ' + this.cls;
44691         }
44692         
44693         var col_sizes = [
44694             'lg',
44695             'md',
44696             'sm',
44697             'xs'
44698         ];
44699         
44700         for(var i = 0; i < col_sizes.length; i++) {
44701             if(this[col_sizes[i]]) {
44702                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44703             }
44704         }
44705         
44706         var cfg = {
44707             tag: 'div',
44708             cls: cls,
44709             cn: [
44710                 {
44711                     tag: 'div',
44712                     cls: 'roo-signature-body',
44713                     cn: [
44714                         {
44715                             tag: 'canvas',
44716                             cls: 'roo-signature-body-canvas',
44717                             height: this.canvas_height,
44718                             width: this.canvas_width
44719                         }
44720                     ]
44721                 },
44722                 {
44723                     tag: 'input',
44724                     type: 'file',
44725                     style: 'display: none'
44726                 }
44727             ]
44728         };
44729         
44730         return cfg;
44731     },
44732     
44733     initEvents: function() 
44734     {
44735         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44736         
44737         var canvas = this.canvasEl();
44738         
44739         // mouse && touch event swapping...
44740         canvas.dom.style.touchAction = 'none';
44741         canvas.dom.style.msTouchAction = 'none';
44742         
44743         this.mouse_btn_down = false;
44744         canvas.on('mousedown', this._handleMouseDown, this);
44745         canvas.on('mousemove', this._handleMouseMove, this);
44746         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44747         
44748         if (window.PointerEvent) {
44749             canvas.on('pointerdown', this._handleMouseDown, this);
44750             canvas.on('pointermove', this._handleMouseMove, this);
44751             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44752         }
44753         
44754         if ('ontouchstart' in window) {
44755             canvas.on('touchstart', this._handleTouchStart, this);
44756             canvas.on('touchmove', this._handleTouchMove, this);
44757             canvas.on('touchend', this._handleTouchEnd, this);
44758         }
44759         
44760         Roo.EventManager.onWindowResize(this.resize, this, true);
44761         
44762         // file input event
44763         this.fileEl().on('change', this.uploadImage, this);
44764         
44765         this.clear();
44766         
44767         this.resize();
44768     },
44769     
44770     resize: function(){
44771         
44772         var canvas = this.canvasEl().dom;
44773         var ctx = this.canvasElCtx();
44774         var img_data = false;
44775         
44776         if(canvas.width > 0) {
44777             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44778         }
44779         // setting canvas width will clean img data
44780         canvas.width = 0;
44781         
44782         var style = window.getComputedStyle ? 
44783             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44784             
44785         var padding_left = parseInt(style.paddingLeft) || 0;
44786         var padding_right = parseInt(style.paddingRight) || 0;
44787         
44788         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44789         
44790         if(img_data) {
44791             ctx.putImageData(img_data, 0, 0);
44792         }
44793     },
44794     
44795     _handleMouseDown: function(e)
44796     {
44797         if (e.browserEvent.which === 1) {
44798             this.mouse_btn_down = true;
44799             this.strokeBegin(e);
44800         }
44801     },
44802     
44803     _handleMouseMove: function (e)
44804     {
44805         if (this.mouse_btn_down) {
44806             this.strokeMoveUpdate(e);
44807         }
44808     },
44809     
44810     _handleMouseUp: function (e)
44811     {
44812         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44813             this.mouse_btn_down = false;
44814             this.strokeEnd(e);
44815         }
44816     },
44817     
44818     _handleTouchStart: function (e) {
44819         
44820         e.preventDefault();
44821         if (e.browserEvent.targetTouches.length === 1) {
44822             // var touch = e.browserEvent.changedTouches[0];
44823             // this.strokeBegin(touch);
44824             
44825              this.strokeBegin(e); // assume e catching the correct xy...
44826         }
44827     },
44828     
44829     _handleTouchMove: function (e) {
44830         e.preventDefault();
44831         // var touch = event.targetTouches[0];
44832         // _this._strokeMoveUpdate(touch);
44833         this.strokeMoveUpdate(e);
44834     },
44835     
44836     _handleTouchEnd: function (e) {
44837         var wasCanvasTouched = e.target === this.canvasEl().dom;
44838         if (wasCanvasTouched) {
44839             e.preventDefault();
44840             // var touch = event.changedTouches[0];
44841             // _this._strokeEnd(touch);
44842             this.strokeEnd(e);
44843         }
44844     },
44845     
44846     reset: function () {
44847         this._lastPoints = [];
44848         this._lastVelocity = 0;
44849         this._lastWidth = (this.min_width + this.max_width) / 2;
44850         this.canvasElCtx().fillStyle = this.dot_color;
44851     },
44852     
44853     strokeMoveUpdate: function(e)
44854     {
44855         this.strokeUpdate(e);
44856         
44857         if (this.throttle) {
44858             this.throttleStroke(this.strokeUpdate, this.throttle);
44859         }
44860         else {
44861             this.strokeUpdate(e);
44862         }
44863     },
44864     
44865     strokeBegin: function(e)
44866     {
44867         var newPointGroup = {
44868             color: this.dot_color,
44869             points: []
44870         };
44871         
44872         if (typeof this.onBegin === 'function') {
44873             this.onBegin(e);
44874         }
44875         
44876         this.curve_data.push(newPointGroup);
44877         this.reset();
44878         this.strokeUpdate(e);
44879     },
44880     
44881     strokeUpdate: function(e)
44882     {
44883         var rect = this.canvasEl().dom.getBoundingClientRect();
44884         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44885         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44886         var lastPoints = lastPointGroup.points;
44887         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44888         var isLastPointTooClose = lastPoint
44889             ? point.distanceTo(lastPoint) <= this.min_distance
44890             : false;
44891         var color = lastPointGroup.color;
44892         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44893             var curve = this.addPoint(point);
44894             if (!lastPoint) {
44895                 this.drawDot({color: color, point: point});
44896             }
44897             else if (curve) {
44898                 this.drawCurve({color: color, curve: curve});
44899             }
44900             lastPoints.push({
44901                 time: point.time,
44902                 x: point.x,
44903                 y: point.y
44904             });
44905         }
44906     },
44907     
44908     strokeEnd: function(e)
44909     {
44910         this.strokeUpdate(e);
44911         if (typeof this.onEnd === 'function') {
44912             this.onEnd(e);
44913         }
44914     },
44915     
44916     addPoint:  function (point) {
44917         var _lastPoints = this._lastPoints;
44918         _lastPoints.push(point);
44919         if (_lastPoints.length > 2) {
44920             if (_lastPoints.length === 3) {
44921                 _lastPoints.unshift(_lastPoints[0]);
44922             }
44923             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44924             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44925             _lastPoints.shift();
44926             return curve;
44927         }
44928         return null;
44929     },
44930     
44931     calculateCurveWidths: function (startPoint, endPoint) {
44932         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44933             (1 - this.velocity_filter_weight) * this._lastVelocity;
44934
44935         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44936         var widths = {
44937             end: newWidth,
44938             start: this._lastWidth
44939         };
44940         
44941         this._lastVelocity = velocity;
44942         this._lastWidth = newWidth;
44943         return widths;
44944     },
44945     
44946     drawDot: function (_a) {
44947         var color = _a.color, point = _a.point;
44948         var ctx = this.canvasElCtx();
44949         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44950         ctx.beginPath();
44951         this.drawCurveSegment(point.x, point.y, width);
44952         ctx.closePath();
44953         ctx.fillStyle = color;
44954         ctx.fill();
44955     },
44956     
44957     drawCurve: function (_a) {
44958         var color = _a.color, curve = _a.curve;
44959         var ctx = this.canvasElCtx();
44960         var widthDelta = curve.endWidth - curve.startWidth;
44961         var drawSteps = Math.floor(curve.length()) * 2;
44962         ctx.beginPath();
44963         ctx.fillStyle = color;
44964         for (var i = 0; i < drawSteps; i += 1) {
44965         var t = i / drawSteps;
44966         var tt = t * t;
44967         var ttt = tt * t;
44968         var u = 1 - t;
44969         var uu = u * u;
44970         var uuu = uu * u;
44971         var x = uuu * curve.startPoint.x;
44972         x += 3 * uu * t * curve.control1.x;
44973         x += 3 * u * tt * curve.control2.x;
44974         x += ttt * curve.endPoint.x;
44975         var y = uuu * curve.startPoint.y;
44976         y += 3 * uu * t * curve.control1.y;
44977         y += 3 * u * tt * curve.control2.y;
44978         y += ttt * curve.endPoint.y;
44979         var width = curve.startWidth + ttt * widthDelta;
44980         this.drawCurveSegment(x, y, width);
44981         }
44982         ctx.closePath();
44983         ctx.fill();
44984     },
44985     
44986     drawCurveSegment: function (x, y, width) {
44987         var ctx = this.canvasElCtx();
44988         ctx.moveTo(x, y);
44989         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44990         this.is_empty = false;
44991     },
44992     
44993     clear: function()
44994     {
44995         var ctx = this.canvasElCtx();
44996         var canvas = this.canvasEl().dom;
44997         ctx.fillStyle = this.bg_color;
44998         ctx.clearRect(0, 0, canvas.width, canvas.height);
44999         ctx.fillRect(0, 0, canvas.width, canvas.height);
45000         this.curve_data = [];
45001         this.reset();
45002         this.is_empty = true;
45003     },
45004     
45005     fileEl: function()
45006     {
45007         return  this.el.select('input',true).first();
45008     },
45009     
45010     canvasEl: function()
45011     {
45012         return this.el.select('canvas',true).first();
45013     },
45014     
45015     canvasElCtx: function()
45016     {
45017         return this.el.select('canvas',true).first().dom.getContext('2d');
45018     },
45019     
45020     getImage: function(type)
45021     {
45022         if(this.is_empty) {
45023             return false;
45024         }
45025         
45026         // encryption ?
45027         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45028     },
45029     
45030     drawFromImage: function(img_src)
45031     {
45032         var img = new Image();
45033         
45034         img.onload = function(){
45035             this.canvasElCtx().drawImage(img, 0, 0);
45036         }.bind(this);
45037         
45038         img.src = img_src;
45039         
45040         this.is_empty = false;
45041     },
45042     
45043     selectImage: function()
45044     {
45045         this.fileEl().dom.click();
45046     },
45047     
45048     uploadImage: function(e)
45049     {
45050         var reader = new FileReader();
45051         
45052         reader.onload = function(e){
45053             var img = new Image();
45054             img.onload = function(){
45055                 this.reset();
45056                 this.canvasElCtx().drawImage(img, 0, 0);
45057             }.bind(this);
45058             img.src = e.target.result;
45059         }.bind(this);
45060         
45061         reader.readAsDataURL(e.target.files[0]);
45062     },
45063     
45064     // Bezier Point Constructor
45065     Point: (function () {
45066         function Point(x, y, time) {
45067             this.x = x;
45068             this.y = y;
45069             this.time = time || Date.now();
45070         }
45071         Point.prototype.distanceTo = function (start) {
45072             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45073         };
45074         Point.prototype.equals = function (other) {
45075             return this.x === other.x && this.y === other.y && this.time === other.time;
45076         };
45077         Point.prototype.velocityFrom = function (start) {
45078             return this.time !== start.time
45079             ? this.distanceTo(start) / (this.time - start.time)
45080             : 0;
45081         };
45082         return Point;
45083     }()),
45084     
45085     
45086     // Bezier Constructor
45087     Bezier: (function () {
45088         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45089             this.startPoint = startPoint;
45090             this.control2 = control2;
45091             this.control1 = control1;
45092             this.endPoint = endPoint;
45093             this.startWidth = startWidth;
45094             this.endWidth = endWidth;
45095         }
45096         Bezier.fromPoints = function (points, widths, scope) {
45097             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45098             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45099             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45100         };
45101         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45102             var dx1 = s1.x - s2.x;
45103             var dy1 = s1.y - s2.y;
45104             var dx2 = s2.x - s3.x;
45105             var dy2 = s2.y - s3.y;
45106             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45107             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45108             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45109             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45110             var dxm = m1.x - m2.x;
45111             var dym = m1.y - m2.y;
45112             var k = l2 / (l1 + l2);
45113             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45114             var tx = s2.x - cm.x;
45115             var ty = s2.y - cm.y;
45116             return {
45117                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45118                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45119             };
45120         };
45121         Bezier.prototype.length = function () {
45122             var steps = 10;
45123             var length = 0;
45124             var px;
45125             var py;
45126             for (var i = 0; i <= steps; i += 1) {
45127                 var t = i / steps;
45128                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45129                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45130                 if (i > 0) {
45131                     var xdiff = cx - px;
45132                     var ydiff = cy - py;
45133                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45134                 }
45135                 px = cx;
45136                 py = cy;
45137             }
45138             return length;
45139         };
45140         Bezier.prototype.point = function (t, start, c1, c2, end) {
45141             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45142             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45143             + (3.0 * c2 * (1.0 - t) * t * t)
45144             + (end * t * t * t);
45145         };
45146         return Bezier;
45147     }()),
45148     
45149     throttleStroke: function(fn, wait) {
45150       if (wait === void 0) { wait = 250; }
45151       var previous = 0;
45152       var timeout = null;
45153       var result;
45154       var storedContext;
45155       var storedArgs;
45156       var later = function () {
45157           previous = Date.now();
45158           timeout = null;
45159           result = fn.apply(storedContext, storedArgs);
45160           if (!timeout) {
45161               storedContext = null;
45162               storedArgs = [];
45163           }
45164       };
45165       return function wrapper() {
45166           var args = [];
45167           for (var _i = 0; _i < arguments.length; _i++) {
45168               args[_i] = arguments[_i];
45169           }
45170           var now = Date.now();
45171           var remaining = wait - (now - previous);
45172           storedContext = this;
45173           storedArgs = args;
45174           if (remaining <= 0 || remaining > wait) {
45175               if (timeout) {
45176                   clearTimeout(timeout);
45177                   timeout = null;
45178               }
45179               previous = now;
45180               result = fn.apply(storedContext, storedArgs);
45181               if (!timeout) {
45182                   storedContext = null;
45183                   storedArgs = [];
45184               }
45185           }
45186           else if (!timeout) {
45187               timeout = window.setTimeout(later, remaining);
45188           }
45189           return result;
45190       };
45191   }
45192   
45193 });
45194
45195  
45196
45197  // old names for form elements
45198 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
45199 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
45200 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
45201 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
45202 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
45203 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
45204 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
45205 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
45206 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
45207 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
45208 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
45209 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
45210 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
45211 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
45212 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
45213 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
45214 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
45215 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
45216 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
45217 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
45218 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
45219 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
45220 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
45221 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
45222 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
45223 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
45224
45225 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
45226 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45227
45228 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
45229 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
45230
45231 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
45232 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45233 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
45234 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
45235