popover column resizing on grids
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
21  * Based on:
22  * Ext JS Library 1.1.1
23  * Copyright(c) 2006-2007, Ext JS, LLC.
24  *
25  * Originally Released Under LGPL - original licence link has changed is not relivant.
26  *
27  * Fork - LGPL
28  * <script type="text/javascript">
29  */
30
31
32 /**
33  * @class Roo.Shadow
34  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
35  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
36  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
37  * @constructor
38  * Create a new Shadow
39  * @param {Object} config The config object
40  */
41 Roo.Shadow = function(config){
42     Roo.apply(this, config);
43     if(typeof this.mode != "string"){
44         this.mode = this.defaultMode;
45     }
46     var o = this.offset, a = {h: 0};
47     var rad = Math.floor(this.offset/2);
48     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49         case "drop":
50             a.w = 0;
51             a.l = a.t = o;
52             a.t -= 1;
53             if(Roo.isIE){
54                 a.l -= this.offset + rad;
55                 a.t -= this.offset + rad;
56                 a.w -= rad;
57                 a.h -= rad;
58                 a.t += 1;
59             }
60         break;
61         case "sides":
62             a.w = (o*2);
63             a.l = -o;
64             a.t = o-1;
65             if(Roo.isIE){
66                 a.l -= (this.offset - rad);
67                 a.t -= this.offset + rad;
68                 a.l += 1;
69                 a.w -= (this.offset - rad)*2;
70                 a.w -= rad + 1;
71                 a.h -= 1;
72             }
73         break;
74         case "frame":
75             a.w = a.h = (o*2);
76             a.l = a.t = -o;
77             a.t += 1;
78             a.h -= 2;
79             if(Roo.isIE){
80                 a.l -= (this.offset - rad);
81                 a.t -= (this.offset - rad);
82                 a.l += 1;
83                 a.w -= (this.offset + rad + 1);
84                 a.h -= (this.offset + rad);
85                 a.h += 1;
86             }
87         break;
88     };
89
90     this.adjusts = a;
91 };
92
93 Roo.Shadow.prototype = {
94     /**
95      * @cfg {String} mode
96      * The shadow display mode.  Supports the following options:<br />
97      * sides: Shadow displays on both sides and bottom only<br />
98      * frame: Shadow displays equally on all four sides<br />
99      * drop: Traditional bottom-right drop shadow (default)
100      */
101     mode: false,
102     /**
103      * @cfg {String} offset
104      * The number of pixels to offset the shadow from the element (defaults to 4)
105      */
106     offset: 4,
107
108     // private
109     defaultMode: "drop",
110
111     /**
112      * Displays the shadow under the target element
113      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
114      */
115     show : function(target){
116         target = Roo.get(target);
117         if(!this.el){
118             this.el = Roo.Shadow.Pool.pull();
119             if(this.el.dom.nextSibling != target.dom){
120                 this.el.insertBefore(target);
121             }
122         }
123         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
124         if(Roo.isIE){
125             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
126         }
127         this.realign(
128             target.getLeft(true),
129             target.getTop(true),
130             target.getWidth(),
131             target.getHeight()
132         );
133         this.el.dom.style.display = "block";
134     },
135
136     /**
137      * Returns true if the shadow is visible, else false
138      */
139     isVisible : function(){
140         return this.el ? true : false;  
141     },
142
143     /**
144      * Direct alignment when values are already available. Show must be called at least once before
145      * calling this method to ensure it is initialized.
146      * @param {Number} left The target element left position
147      * @param {Number} top The target element top position
148      * @param {Number} width The target element width
149      * @param {Number} height The target element height
150      */
151     realign : function(l, t, w, h){
152         if(!this.el){
153             return;
154         }
155         var a = this.adjusts, d = this.el.dom, s = d.style;
156         var iea = 0;
157         s.left = (l+a.l)+"px";
158         s.top = (t+a.t)+"px";
159         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
160  
161         if(s.width != sws || s.height != shs){
162             s.width = sws;
163             s.height = shs;
164             if(!Roo.isIE){
165                 var cn = d.childNodes;
166                 var sww = Math.max(0, (sw-12))+"px";
167                 cn[0].childNodes[1].style.width = sww;
168                 cn[1].childNodes[1].style.width = sww;
169                 cn[2].childNodes[1].style.width = sww;
170                 cn[1].style.height = Math.max(0, (sh-12))+"px";
171             }
172         }
173     },
174
175     /**
176      * Hides this shadow
177      */
178     hide : function(){
179         if(this.el){
180             this.el.dom.style.display = "none";
181             Roo.Shadow.Pool.push(this.el);
182             delete this.el;
183         }
184     },
185
186     /**
187      * Adjust the z-index of this shadow
188      * @param {Number} zindex The new z-index
189      */
190     setZIndex : function(z){
191         this.zIndex = z;
192         if(this.el){
193             this.el.setStyle("z-index", z);
194         }
195     }
196 };
197
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
200     var p = [];
201     var markup = Roo.isIE ?
202                  '<div class="x-ie-shadow"></div>' :
203                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204     return {
205         pull : function(){
206             var sh = p.shift();
207             if(!sh){
208                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209                 sh.autoBoxAdjust = false;
210             }
211             return sh;
212         },
213
214         push : function(sh){
215             p.push(sh);
216         }
217     };
218 }();/*
219  * - LGPL
220  *
221  * base class for bootstrap elements.
222  * 
223  */
224
225 Roo.bootstrap = Roo.bootstrap || {};
226 /**
227  * @class Roo.bootstrap.Component
228  * @extends Roo.Component
229  * @abstract
230  * @children Roo.bootstrap.Component
231  * Bootstrap Component base class
232  * @cfg {String} cls css class
233  * @cfg {String} style any extra css
234  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
236  * @cfg {string} dataId cutomer id
237  * @cfg {string} name Specifies name attribute
238  * @cfg {string} tooltip  Text for the tooltip
239  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
240  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
241  
242  * @constructor
243  * Do not use directly - it does not do anything..
244  * @param {Object} config The config object
245  */
246
247
248
249 Roo.bootstrap.Component = function(config){
250     Roo.bootstrap.Component.superclass.constructor.call(this, config);
251        
252     this.addEvents({
253         /**
254          * @event childrenrendered
255          * Fires when the children have been rendered..
256          * @param {Roo.bootstrap.Component} this
257          */
258         "childrenrendered" : true
259         
260         
261         
262     });
263     
264     
265 };
266
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
268     
269     
270     allowDomMove : false, // to stop relocations in parent onRender...
271     
272     cls : false,
273     
274     style : false,
275     
276     autoCreate : false,
277     
278     tooltip : null,
279     /**
280      * Initialize Events for the element
281      */
282     initEvents : function() { },
283     
284     xattr : false,
285     
286     parentId : false,
287     
288     can_build_overlaid : true,
289     
290     container_method : false,
291     
292     dataId : false,
293     
294     name : false,
295     
296     parent: function() {
297         // returns the parent component..
298         return Roo.ComponentMgr.get(this.parentId)
299         
300         
301     },
302     
303     // private
304     onRender : function(ct, position)
305     {
306        // Roo.log("Call onRender: " + this.xtype);
307         
308         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
309         
310         if(this.el){
311             if (this.el.attr('xtype')) {
312                 this.el.attr('xtypex', this.el.attr('xtype'));
313                 this.el.dom.removeAttribute('xtype');
314                 
315                 this.initEvents();
316             }
317             
318             return;
319         }
320         
321          
322         
323         var cfg = Roo.apply({},  this.getAutoCreate());
324         
325         cfg.id = this.id || Roo.id();
326         
327         // fill in the extra attributes 
328         if (this.xattr && typeof(this.xattr) =='object') {
329             for (var i in this.xattr) {
330                 cfg[i] = this.xattr[i];
331             }
332         }
333         
334         if(this.dataId){
335             cfg.dataId = this.dataId;
336         }
337         
338         if (this.cls) {
339             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
340         }
341         
342         if (this.style) { // fixme needs to support more complex style data.
343             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
344         }
345         
346         if(this.name){
347             cfg.name = this.name;
348         }
349         
350         this.el = ct.createChild(cfg, position);
351         
352         if (this.tooltip) {
353             this.tooltipEl().attr('tooltip', this.tooltip);
354         }
355         
356         if(this.tabIndex !== undefined){
357             this.el.dom.setAttribute('tabIndex', this.tabIndex);
358         }
359         
360         this.initEvents();
361         
362     },
363     /**
364      * Fetch the element to add children to
365      * @return {Roo.Element} defaults to this.el
366      */
367     getChildContainer : function()
368     {
369         return this.el;
370     },
371     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
372     {
373         return Roo.get(document.body);
374     },
375     
376     /**
377      * Fetch the element to display the tooltip on.
378      * @return {Roo.Element} defaults to this.el
379      */
380     tooltipEl : function()
381     {
382         return this.el;
383     },
384         
385     addxtype  : function(tree,cntr)
386     {
387         var cn = this;
388         
389         cn = Roo.factory(tree);
390         //Roo.log(['addxtype', cn]);
391            
392         cn.parentType = this.xtype; //??
393         cn.parentId = this.id;
394         
395         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396         if (typeof(cn.container_method) == 'string') {
397             cntr = cn.container_method;
398         }
399         
400         
401         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
402         
403         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
404         
405         var build_from_html =  Roo.XComponent.build_from_html;
406           
407         var is_body  = (tree.xtype == 'Body') ;
408           
409         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
410           
411         var self_cntr_el = Roo.get(this[cntr](false));
412         
413         // do not try and build conditional elements 
414         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
415             return false;
416         }
417         
418         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420                 return this.addxtypeChild(tree,cntr, is_body);
421             }
422             
423             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
424                 
425             if(echild){
426                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
427             }
428             
429             Roo.log('skipping render');
430             return cn;
431             
432         }
433         
434         var ret = false;
435         if (!build_from_html) {
436             return false;
437         }
438         
439         // this i think handles overlaying multiple children of the same type
440         // with the sam eelement.. - which might be buggy..
441         while (true) {
442             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
443             
444             if (!echild) {
445                 break;
446             }
447             
448             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
449                 break;
450             }
451             
452             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453         }
454        
455         return ret;
456     },
457     
458     
459     addxtypeChild : function (tree, cntr, is_body)
460     {
461         Roo.debug && Roo.log('addxtypeChild:' + cntr);
462         var cn = this;
463         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
464         
465         
466         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467                     (typeof(tree['flexy:foreach']) != 'undefined');
468           
469     
470         
471         skip_children = false;
472         // render the element if it's not BODY.
473         if (!is_body) {
474             
475             // if parent was disabled, then do not try and create the children..
476             if(!this[cntr](true)){
477                 tree.items = [];
478                 return tree;
479             }
480            
481             cn = Roo.factory(tree);
482            
483             cn.parentType = this.xtype; //??
484             cn.parentId = this.id;
485             
486             var build_from_html =  Roo.XComponent.build_from_html;
487             
488             
489             // does the container contain child eleemnts with 'xtype' attributes.
490             // that match this xtype..
491             // note - when we render we create these as well..
492             // so we should check to see if body has xtype set.
493             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
494                
495                 var self_cntr_el = Roo.get(this[cntr](false));
496                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
497                 if (echild) { 
498                     //Roo.log(Roo.XComponent.build_from_html);
499                     //Roo.log("got echild:");
500                     //Roo.log(echild);
501                 }
502                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503                 // and are not displayed -this causes this to use up the wrong element when matching.
504                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
505                 
506                 
507                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
509                   
510                   
511                   
512                     cn.el = echild;
513                   //  Roo.log("GOT");
514                     //echild.dom.removeAttribute('xtype');
515                 } else {
516                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517                     Roo.debug && Roo.log(self_cntr_el);
518                     Roo.debug && Roo.log(echild);
519                     Roo.debug && Roo.log(cn);
520                 }
521             }
522            
523             
524            
525             // if object has flexy:if - then it may or may not be rendered.
526             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
527                 // skip a flexy if element.
528                 Roo.debug && Roo.log('skipping render');
529                 Roo.debug && Roo.log(tree);
530                 if (!cn.el) {
531                     Roo.debug && Roo.log('skipping all children');
532                     skip_children = true;
533                 }
534                 
535              } else {
536                  
537                 // actually if flexy:foreach is found, we really want to create 
538                 // multiple copies here...
539                 //Roo.log('render');
540                 //Roo.log(this[cntr]());
541                 // some elements do not have render methods.. like the layouts...
542                 /*
543                 if(this[cntr](true) === false){
544                     cn.items = [];
545                     return cn;
546                 }
547                 */
548                 cn.render && cn.render(this[cntr](true));
549                 
550              }
551             // then add the element..
552         }
553          
554         // handle the kids..
555         
556         var nitems = [];
557         /*
558         if (typeof (tree.menu) != 'undefined') {
559             tree.menu.parentType = cn.xtype;
560             tree.menu.triggerEl = cn.el;
561             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
562             
563         }
564         */
565         if (!tree.items || !tree.items.length) {
566             cn.items = nitems;
567             //Roo.log(["no children", this]);
568             
569             return cn;
570         }
571          
572         var items = tree.items;
573         delete tree.items;
574         
575         //Roo.log(items.length);
576             // add the items..
577         if (!skip_children) {    
578             for(var i =0;i < items.length;i++) {
579               //  Roo.log(['add child', items[i]]);
580                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
581             }
582         }
583         
584         cn.items = nitems;
585         
586         //Roo.log("fire childrenrendered");
587         
588         cn.fireEvent('childrenrendered', this);
589         
590         return cn;
591     },
592     
593     /**
594      * Set the element that will be used to show or hide
595      */
596     setVisibilityEl : function(el)
597     {
598         this.visibilityEl = el;
599     },
600     
601      /**
602      * Get the element that will be used to show or hide
603      */
604     getVisibilityEl : function()
605     {
606         if (typeof(this.visibilityEl) == 'object') {
607             return this.visibilityEl;
608         }
609         
610         if (typeof(this.visibilityEl) == 'string') {
611             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612         }
613         
614         return this.getEl();
615     },
616     
617     /**
618      * Show a component - removes 'hidden' class
619      */
620     show : function()
621     {
622         if(!this.getVisibilityEl()){
623             return;
624         }
625          
626         this.getVisibilityEl().removeClass(['hidden','d-none']);
627         
628         this.fireEvent('show', this);
629         
630         
631     },
632     /**
633      * Hide a component - adds 'hidden' class
634      */
635     hide: function()
636     {
637         if(!this.getVisibilityEl()){
638             return;
639         }
640         
641         this.getVisibilityEl().addClass(['hidden','d-none']);
642         
643         this.fireEvent('hide', this);
644         
645     }
646 });
647
648  /*
649  * - LGPL
650  *
651  * element
652  * 
653  */
654
655 /**
656  * @class Roo.bootstrap.Element
657  * @extends Roo.bootstrap.Component
658  * @children Roo.bootstrap.Component
659  * Bootstrap Element class (basically a DIV used to make random stuff )
660  * 
661  * @cfg {String} html contents of the element
662  * @cfg {String} tag tag of the element
663  * @cfg {String} cls class of the element
664  * @cfg {Boolean} preventDefault (true|false) default false
665  * @cfg {Boolean} clickable (true|false) default false
666  * @cfg {String} role default blank - set to button to force cursor pointer
667  
668  * 
669  * @constructor
670  * Create a new Element
671  * @param {Object} config The config object
672  */
673
674 Roo.bootstrap.Element = function(config){
675     Roo.bootstrap.Element.superclass.constructor.call(this, config);
676     
677     this.addEvents({
678         // raw events
679         /**
680          * @event click
681          * When a element is chick
682          * @param {Roo.bootstrap.Element} this
683          * @param {Roo.EventObject} e
684          */
685         "click" : true 
686         
687       
688     });
689 };
690
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
692     
693     tag: 'div',
694     cls: '',
695     html: '',
696     preventDefault: false, 
697     clickable: false,
698     tapedTwice : false,
699     role : false,
700     
701     getAutoCreate : function(){
702         
703         var cfg = {
704             tag: this.tag,
705             // cls: this.cls, double assign in parent class Component.js :: onRender
706             html: this.html
707         };
708         if (this.role !== false) {
709             cfg.role = this.role;
710         }
711         
712         return cfg;
713     },
714     
715     initEvents: function() 
716     {
717         Roo.bootstrap.Element.superclass.initEvents.call(this);
718         
719         if(this.clickable){
720             this.el.on('click', this.onClick, this);
721         }
722         
723         
724     },
725     
726     onClick : function(e)
727     {
728         if(this.preventDefault){
729             e.preventDefault();
730         }
731         
732         this.fireEvent('click', this, e); // why was this double click before?
733     },
734     
735     
736     
737
738     
739     
740     getValue : function()
741     {
742         return this.el.dom.innerHTML;
743     },
744     
745     setValue : function(value)
746     {
747         this.el.dom.innerHTML = value;
748     }
749    
750 });
751
752  
753
754  /*
755  * - LGPL
756  *
757  * dropable area
758  * 
759  */
760
761 /**
762  * @class Roo.bootstrap.DropTarget
763  * @extends Roo.bootstrap.Element
764  * Bootstrap DropTarget class
765  
766  * @cfg {string} name dropable name
767  * 
768  * @constructor
769  * Create a new Dropable Area
770  * @param {Object} config The config object
771  */
772
773 Roo.bootstrap.DropTarget = function(config){
774     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
775     
776     this.addEvents({
777         // raw events
778         /**
779          * @event click
780          * When a element is chick
781          * @param {Roo.bootstrap.Element} this
782          * @param {Roo.EventObject} e
783          */
784         "drop" : true
785     });
786 };
787
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
789     
790     
791     getAutoCreate : function(){
792         
793          
794     },
795     
796     initEvents: function() 
797     {
798         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
800             ddGroup: this.name,
801             listeners : {
802                 drop : this.dragDrop.createDelegate(this),
803                 enter : this.dragEnter.createDelegate(this),
804                 out : this.dragOut.createDelegate(this),
805                 over : this.dragOver.createDelegate(this)
806             }
807             
808         });
809         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
810     },
811     
812     dragDrop : function(source,e,data)
813     {
814         // user has to decide how to impliment this.
815         Roo.log('drop');
816         Roo.log(this);
817         //this.fireEvent('drop', this, source, e ,data);
818         return false;
819     },
820     
821     dragEnter : function(n, dd, e, data)
822     {
823         // probably want to resize the element to match the dropped element..
824         Roo.log("enter");
825         this.originalSize = this.el.getSize();
826         this.el.setSize( n.el.getSize());
827         this.dropZone.DDM.refreshCache(this.name);
828         Roo.log([n, dd, e, data]);
829     },
830     
831     dragOut : function(value)
832     {
833         // resize back to normal
834         Roo.log("out");
835         this.el.setSize(this.originalSize);
836         this.dropZone.resetConstraints();
837     },
838     
839     dragOver : function()
840     {
841         // ??? do nothing?
842     }
843    
844 });
845
846  
847
848  /*
849  * - LGPL
850  *
851  * Body
852  *
853  */
854
855 /**
856  * @class Roo.bootstrap.Body
857  * @extends Roo.bootstrap.Component
858  * @children Roo.bootstrap.Component 
859  * @parent none builder
860  * Bootstrap Body class
861  *
862  * @constructor
863  * Create a new body
864  * @param {Object} config The config object
865  */
866
867 Roo.bootstrap.Body = function(config){
868
869     config = config || {};
870
871     Roo.bootstrap.Body.superclass.constructor.call(this, config);
872     this.el = Roo.get(config.el ? config.el : document.body );
873     if (this.cls && this.cls.length) {
874         Roo.get(document.body).addClass(this.cls);
875     }
876 };
877
878 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
879
880     is_body : true,// just to make sure it's constructed?
881
882         autoCreate : {
883         cls: 'container'
884     },
885     onRender : function(ct, position)
886     {
887        /* Roo.log("Roo.bootstrap.Body - onRender");
888         if (this.cls && this.cls.length) {
889             Roo.get(document.body).addClass(this.cls);
890         }
891         // style??? xttr???
892         */
893     }
894
895
896
897
898 });
899 /*
900  * - LGPL
901  *
902  * button group
903  * 
904  */
905
906
907 /**
908  * @class Roo.bootstrap.ButtonGroup
909  * @extends Roo.bootstrap.Component
910  * Bootstrap ButtonGroup class
911  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
912  * 
913  * @cfg {String} size lg | sm | xs (default empty normal)
914  * @cfg {String} align vertical | justified  (default none)
915  * @cfg {String} direction up | down (default down)
916  * @cfg {Boolean} toolbar false | true
917  * @cfg {Boolean} btn true | false
918  * 
919  * 
920  * @constructor
921  * Create a new Input
922  * @param {Object} config The config object
923  */
924
925 Roo.bootstrap.ButtonGroup = function(config){
926     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
927 };
928
929 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
930     
931     size: '',
932     align: '',
933     direction: '',
934     toolbar: false,
935     btn: true,
936
937     getAutoCreate : function(){
938         var cfg = {
939             cls: 'btn-group',
940             html : null
941         };
942         
943         cfg.html = this.html || cfg.html;
944         
945         if (this.toolbar) {
946             cfg = {
947                 cls: 'btn-toolbar',
948                 html: null
949             };
950             
951             return cfg;
952         }
953         
954         if (['vertical','justified'].indexOf(this.align)!==-1) {
955             cfg.cls = 'btn-group-' + this.align;
956             
957             if (this.align == 'justified') {
958                 console.log(this.items);
959             }
960         }
961         
962         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
963             cfg.cls += ' btn-group-' + this.size;
964         }
965         
966         if (this.direction == 'up') {
967             cfg.cls += ' dropup' ;
968         }
969         
970         return cfg;
971     },
972     /**
973      * Add a button to the group (similar to NavItem API.)
974      */
975     addItem : function(cfg)
976     {
977         var cn = new Roo.bootstrap.Button(cfg);
978         //this.register(cn);
979         cn.parentId = this.id;
980         cn.onRender(this.el, null);
981         return cn;
982     }
983    
984 });
985
986  /*
987  * - LGPL
988  *
989  * button
990  * 
991  */
992
993 /**
994  * @class Roo.bootstrap.Button
995  * @extends Roo.bootstrap.Component
996  * Bootstrap Button class
997  * @cfg {String} html The button content
998  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
999  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1000  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1001  * @cfg {String} size (lg|sm|xs)
1002  * @cfg {String} tag (a|input|submit)
1003  * @cfg {String} href empty or href
1004  * @cfg {Boolean} disabled default false;
1005  * @cfg {Boolean} isClose default false;
1006  * @cfg {String} glyphicon depricated - use fa
1007  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1008  * @cfg {String} badge text for badge
1009  * @cfg {String} theme (default|glow)  
1010  * @cfg {Boolean} inverse dark themed version
1011  * @cfg {Boolean} toggle is it a slidy toggle button
1012  * @cfg {Boolean} pressed   default null - if the button ahs active state
1013  * @cfg {String} ontext text for on slidy toggle state
1014  * @cfg {String} offtext text for off slidy toggle state
1015  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1016  * @cfg {Boolean} removeClass remove the standard class..
1017  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1018  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1019  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1020
1021  * @constructor
1022  * Create a new button
1023  * @param {Object} config The config object
1024  */
1025
1026
1027 Roo.bootstrap.Button = function(config){
1028     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1029     
1030     this.addEvents({
1031         // raw events
1032         /**
1033          * @event click
1034          * When a button is pressed
1035          * @param {Roo.bootstrap.Button} btn
1036          * @param {Roo.EventObject} e
1037          */
1038         "click" : true,
1039         /**
1040          * @event dblclick
1041          * When a button is double clicked
1042          * @param {Roo.bootstrap.Button} btn
1043          * @param {Roo.EventObject} e
1044          */
1045         "dblclick" : true,
1046          /**
1047          * @event toggle
1048          * After the button has been toggles
1049          * @param {Roo.bootstrap.Button} btn
1050          * @param {Roo.EventObject} e
1051          * @param {boolean} pressed (also available as button.pressed)
1052          */
1053         "toggle" : true
1054     });
1055 };
1056
1057 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1058     html: false,
1059     active: false,
1060     weight: '',
1061     badge_weight: '',
1062     outline : false,
1063     size: '',
1064     tag: 'button',
1065     href: '',
1066     disabled: false,
1067     isClose: false,
1068     glyphicon: '',
1069     fa: '',
1070     badge: '',
1071     theme: 'default',
1072     inverse: false,
1073     
1074     toggle: false,
1075     ontext: 'ON',
1076     offtext: 'OFF',
1077     defaulton: true,
1078     preventDefault: true,
1079     removeClass: false,
1080     name: false,
1081     target: false,
1082     group : false,
1083      
1084     pressed : null,
1085      
1086     
1087     getAutoCreate : function(){
1088         
1089         var cfg = {
1090             tag : 'button',
1091             cls : 'roo-button',
1092             html: ''
1093         };
1094         
1095         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1096             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1097             this.tag = 'button';
1098         } else {
1099             cfg.tag = this.tag;
1100         }
1101         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1102         
1103         if (this.toggle == true) {
1104             cfg={
1105                 tag: 'div',
1106                 cls: 'slider-frame roo-button',
1107                 cn: [
1108                     {
1109                         tag: 'span',
1110                         'data-on-text':'ON',
1111                         'data-off-text':'OFF',
1112                         cls: 'slider-button',
1113                         html: this.offtext
1114                     }
1115                 ]
1116             };
1117             // why are we validating the weights?
1118             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119                 cfg.cls +=  ' ' + this.weight;
1120             }
1121             
1122             return cfg;
1123         }
1124         
1125         if (this.isClose) {
1126             cfg.cls += ' close';
1127             
1128             cfg["aria-hidden"] = true;
1129             
1130             cfg.html = "&times;";
1131             
1132             return cfg;
1133         }
1134              
1135         
1136         if (this.theme==='default') {
1137             cfg.cls = 'btn roo-button';
1138             
1139             //if (this.parentType != 'Navbar') {
1140             this.weight = this.weight.length ?  this.weight : 'default';
1141             //}
1142             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1143                 
1144                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1145                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1146                 cfg.cls += ' btn-' + outline + weight;
1147                 if (this.weight == 'default') {
1148                     // BC
1149                     cfg.cls += ' btn-' + this.weight;
1150                 }
1151             }
1152         } else if (this.theme==='glow') {
1153             
1154             cfg.tag = 'a';
1155             cfg.cls = 'btn-glow roo-button';
1156             
1157             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1158                 
1159                 cfg.cls += ' ' + this.weight;
1160             }
1161         }
1162    
1163         
1164         if (this.inverse) {
1165             this.cls += ' inverse';
1166         }
1167         
1168         
1169         if (this.active || this.pressed === true) {
1170             cfg.cls += ' active';
1171         }
1172         
1173         if (this.disabled) {
1174             cfg.disabled = 'disabled';
1175         }
1176         
1177         if (this.items) {
1178             Roo.log('changing to ul' );
1179             cfg.tag = 'ul';
1180             this.glyphicon = 'caret';
1181             if (Roo.bootstrap.version == 4) {
1182                 this.fa = 'caret-down';
1183             }
1184             
1185         }
1186         
1187         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1188          
1189         //gsRoo.log(this.parentType);
1190         if (this.parentType === 'Navbar' && !this.parent().bar) {
1191             Roo.log('changing to li?');
1192             
1193             cfg.tag = 'li';
1194             
1195             cfg.cls = '';
1196             cfg.cn =  [{
1197                 tag : 'a',
1198                 cls : 'roo-button',
1199                 html : this.html,
1200                 href : this.href || '#'
1201             }];
1202             if (this.menu) {
1203                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1204                 cfg.cls += ' dropdown';
1205             }   
1206             
1207             delete cfg.html;
1208             
1209         }
1210         
1211        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1212         
1213         if (this.glyphicon) {
1214             cfg.html = ' ' + cfg.html;
1215             
1216             cfg.cn = [
1217                 {
1218                     tag: 'span',
1219                     cls: 'glyphicon glyphicon-' + this.glyphicon
1220                 }
1221             ];
1222         }
1223         if (this.fa) {
1224             cfg.html = ' ' + cfg.html;
1225             
1226             cfg.cn = [
1227                 {
1228                     tag: 'i',
1229                     cls: 'fa fas fa-' + this.fa
1230                 }
1231             ];
1232         }
1233         
1234         if (this.badge) {
1235             cfg.html += ' ';
1236             
1237             cfg.tag = 'a';
1238             
1239 //            cfg.cls='btn roo-button';
1240             
1241             cfg.href=this.href;
1242             
1243             var value = cfg.html;
1244             
1245             if(this.glyphicon){
1246                 value = {
1247                     tag: 'span',
1248                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1249                     html: this.html
1250                 };
1251             }
1252             if(this.fa){
1253                 value = {
1254                     tag: 'i',
1255                     cls: 'fa fas fa-' + this.fa,
1256                     html: this.html
1257                 };
1258             }
1259             
1260             var bw = this.badge_weight.length ? this.badge_weight :
1261                 (this.weight.length ? this.weight : 'secondary');
1262             bw = bw == 'default' ? 'secondary' : bw;
1263             
1264             cfg.cn = [
1265                 value,
1266                 {
1267                     tag: 'span',
1268                     cls: 'badge badge-' + bw,
1269                     html: this.badge
1270                 }
1271             ];
1272             
1273             cfg.html='';
1274         }
1275         
1276         if (this.menu) {
1277             cfg.cls += ' dropdown';
1278             cfg.html = typeof(cfg.html) != 'undefined' ?
1279                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1280         }
1281         
1282         if (cfg.tag !== 'a' && this.href !== '') {
1283             throw "Tag must be a to set href.";
1284         } else if (this.href.length > 0) {
1285             cfg.href = this.href;
1286         }
1287         
1288         if(this.removeClass){
1289             cfg.cls = '';
1290         }
1291         
1292         if(this.target){
1293             cfg.target = this.target;
1294         }
1295         
1296         return cfg;
1297     },
1298     initEvents: function() {
1299        // Roo.log('init events?');
1300 //        Roo.log(this.el.dom);
1301         // add the menu...
1302         
1303         if (typeof (this.menu) != 'undefined') {
1304             this.menu.parentType = this.xtype;
1305             this.menu.triggerEl = this.el;
1306             this.addxtype(Roo.apply({}, this.menu));
1307         }
1308
1309
1310         if (this.el.hasClass('roo-button')) {
1311              this.el.on('click', this.onClick, this);
1312              this.el.on('dblclick', this.onDblClick, this);
1313         } else {
1314              this.el.select('.roo-button').on('click', this.onClick, this);
1315              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1316              
1317         }
1318         // why?
1319         if(this.removeClass){
1320             this.el.on('click', this.onClick, this);
1321         }
1322         
1323         if (this.group === true) {
1324              if (this.pressed === false || this.pressed === true) {
1325                 // nothing
1326             } else {
1327                 this.pressed = false;
1328                 this.setActive(this.pressed);
1329             }
1330             
1331         }
1332         
1333         this.el.enableDisplayMode();
1334         
1335     },
1336     onClick : function(e)
1337     {
1338         if (this.disabled) {
1339             return;
1340         }
1341         
1342         Roo.log('button on click ');
1343         if(this.preventDefault){
1344             e.preventDefault();
1345         }
1346         
1347         if (this.group) {
1348             if (this.pressed) {
1349                 // do nothing -
1350                 return;
1351             }
1352             this.setActive(true);
1353             var pi = this.parent().items;
1354             for (var i = 0;i < pi.length;i++) {
1355                 if (this == pi[i]) {
1356                     continue;
1357                 }
1358                 if (pi[i].el.hasClass('roo-button')) {
1359                     pi[i].setActive(false);
1360                 }
1361             }
1362             this.fireEvent('click', this, e);            
1363             return;
1364         }
1365         
1366         if (this.pressed === true || this.pressed === false) {
1367             this.toggleActive(e);
1368         }
1369         
1370         
1371         this.fireEvent('click', this, e);
1372     },
1373     onDblClick: function(e)
1374     {
1375         if (this.disabled) {
1376             return;
1377         }
1378         if(this.preventDefault){
1379             e.preventDefault();
1380         }
1381         this.fireEvent('dblclick', this, e);
1382     },
1383     /**
1384      * Enables this button
1385      */
1386     enable : function()
1387     {
1388         this.disabled = false;
1389         this.el.removeClass('disabled');
1390         this.el.dom.removeAttribute("disabled");
1391     },
1392     
1393     /**
1394      * Disable this button
1395      */
1396     disable : function()
1397     {
1398         this.disabled = true;
1399         this.el.addClass('disabled');
1400         this.el.attr("disabled", "disabled")
1401     },
1402      /**
1403      * sets the active state on/off, 
1404      * @param {Boolean} state (optional) Force a particular state
1405      */
1406     setActive : function(v) {
1407         
1408         this.el[v ? 'addClass' : 'removeClass']('active');
1409         this.pressed = v;
1410     },
1411      /**
1412      * toggles the current active state 
1413      */
1414     toggleActive : function(e)
1415     {
1416         this.setActive(!this.pressed); // this modifies pressed...
1417         this.fireEvent('toggle', this, e, this.pressed);
1418     },
1419      /**
1420      * get the current active state
1421      * @return {boolean} true if it's active
1422      */
1423     isActive : function()
1424     {
1425         return this.el.hasClass('active');
1426     },
1427     /**
1428      * set the text of the first selected button
1429      */
1430     setText : function(str)
1431     {
1432         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1433     },
1434     /**
1435      * get the text of the first selected button
1436      */
1437     getText : function()
1438     {
1439         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1440     },
1441     
1442     setWeight : function(str)
1443     {
1444         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1446         this.weight = str;
1447         var outline = this.outline ? 'outline-' : '';
1448         if (str == 'default') {
1449             this.el.addClass('btn-default btn-outline-secondary');        
1450             return;
1451         }
1452         this.el.addClass('btn-' + outline + str);        
1453     }
1454     
1455     
1456 });
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1458
1459 Roo.bootstrap.Button.weights = [
1460     'default',
1461     'secondary' ,
1462     'primary',
1463     'success',
1464     'info',
1465     'warning',
1466     'danger',
1467     'link',
1468     'light',
1469     'dark'              
1470    
1471 ];/*
1472  * - LGPL
1473  *
1474  * column
1475  * 
1476  */
1477
1478 /**
1479  * @class Roo.bootstrap.Column
1480  * @extends Roo.bootstrap.Component
1481  * @children Roo.bootstrap.Component
1482  * Bootstrap Column class
1483  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1491  *
1492  * 
1493  * @cfg {Boolean} hidden (true|false) hide the element
1494  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495  * @cfg {String} fa (ban|check|...) font awesome icon
1496  * @cfg {Number} fasize (1|2|....) font awsome size
1497
1498  * @cfg {String} icon (info-sign|check|...) glyphicon name
1499
1500  * @cfg {String} html content of column.
1501  * 
1502  * @constructor
1503  * Create a new Column
1504  * @param {Object} config The config object
1505  */
1506
1507 Roo.bootstrap.Column = function(config){
1508     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1509 };
1510
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1512     
1513     xs: false,
1514     sm: false,
1515     md: false,
1516     lg: false,
1517     xsoff: false,
1518     smoff: false,
1519     mdoff: false,
1520     lgoff: false,
1521     html: '',
1522     offset: 0,
1523     alert: false,
1524     fa: false,
1525     icon : false,
1526     hidden : false,
1527     fasize : 1,
1528     
1529     getAutoCreate : function(){
1530         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1531         
1532         cfg = {
1533             tag: 'div',
1534             cls: 'column'
1535         };
1536         
1537         var settings=this;
1538         var sizes =   ['xs','sm','md','lg'];
1539         sizes.map(function(size ,ix){
1540             //Roo.log( size + ':' + settings[size]);
1541             
1542             if (settings[size+'off'] !== false) {
1543                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1544             }
1545             
1546             if (settings[size] === false) {
1547                 return;
1548             }
1549             
1550             if (!settings[size]) { // 0 = hidden
1551                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1552                 // bootsrap4
1553                 for (var i = ix; i > -1; i--) {
1554                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1555                 }
1556                 
1557                 
1558                 return;
1559             }
1560             cfg.cls += ' col-' + size + '-' + settings[size] + (
1561                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1562             );
1563             
1564         });
1565         
1566         if (this.hidden) {
1567             cfg.cls += ' hidden';
1568         }
1569         
1570         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571             cfg.cls +=' alert alert-' + this.alert;
1572         }
1573         
1574         
1575         if (this.html.length) {
1576             cfg.html = this.html;
1577         }
1578         if (this.fa) {
1579             var fasize = '';
1580             if (this.fasize > 1) {
1581                 fasize = ' fa-' + this.fasize + 'x';
1582             }
1583             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1584             
1585             
1586         }
1587         if (this.icon) {
1588             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1589         }
1590         
1591         return cfg;
1592     }
1593    
1594 });
1595
1596  
1597
1598  /*
1599  * - LGPL
1600  *
1601  * page container.
1602  * 
1603  */
1604
1605
1606 /**
1607  * @class Roo.bootstrap.Container
1608  * @extends Roo.bootstrap.Component
1609  * @children Roo.bootstrap.Component
1610  * @parent builder
1611  * Bootstrap Container class
1612  * @cfg {Boolean} jumbotron is it a jumbotron element
1613  * @cfg {String} html content of element
1614  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1616  * @cfg {String} header content of header (for panel)
1617  * @cfg {String} footer content of footer (for panel)
1618  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619  * @cfg {String} tag (header|aside|section) type of HTML tag.
1620  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621  * @cfg {String} fa font awesome icon
1622  * @cfg {String} icon (info-sign|check|...) glyphicon name
1623  * @cfg {Boolean} hidden (true|false) hide the element
1624  * @cfg {Boolean} expandable (true|false) default false
1625  * @cfg {Boolean} expanded (true|false) default true
1626  * @cfg {String} rheader contet on the right of header
1627  * @cfg {Boolean} clickable (true|false) default false
1628
1629  *     
1630  * @constructor
1631  * Create a new Container
1632  * @param {Object} config The config object
1633  */
1634
1635 Roo.bootstrap.Container = function(config){
1636     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1637     
1638     this.addEvents({
1639         // raw events
1640          /**
1641          * @event expand
1642          * After the panel has been expand
1643          * 
1644          * @param {Roo.bootstrap.Container} this
1645          */
1646         "expand" : true,
1647         /**
1648          * @event collapse
1649          * After the panel has been collapsed
1650          * 
1651          * @param {Roo.bootstrap.Container} this
1652          */
1653         "collapse" : true,
1654         /**
1655          * @event click
1656          * When a element is chick
1657          * @param {Roo.bootstrap.Container} this
1658          * @param {Roo.EventObject} e
1659          */
1660         "click" : true
1661     });
1662 };
1663
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1665     
1666     jumbotron : false,
1667     well: '',
1668     panel : '',
1669     header: '',
1670     footer : '',
1671     sticky: '',
1672     tag : false,
1673     alert : false,
1674     fa: false,
1675     icon : false,
1676     expandable : false,
1677     rheader : '',
1678     expanded : true,
1679     clickable: false,
1680   
1681      
1682     getChildContainer : function() {
1683         
1684         if(!this.el){
1685             return false;
1686         }
1687         
1688         if (this.panel.length) {
1689             return this.el.select('.panel-body',true).first();
1690         }
1691         
1692         return this.el;
1693     },
1694     
1695     
1696     getAutoCreate : function(){
1697         
1698         var cfg = {
1699             tag : this.tag || 'div',
1700             html : '',
1701             cls : ''
1702         };
1703         if (this.jumbotron) {
1704             cfg.cls = 'jumbotron';
1705         }
1706         
1707         
1708         
1709         // - this is applied by the parent..
1710         //if (this.cls) {
1711         //    cfg.cls = this.cls + '';
1712         //}
1713         
1714         if (this.sticky.length) {
1715             
1716             var bd = Roo.get(document.body);
1717             if (!bd.hasClass('bootstrap-sticky')) {
1718                 bd.addClass('bootstrap-sticky');
1719                 Roo.select('html',true).setStyle('height', '100%');
1720             }
1721              
1722             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1723         }
1724         
1725         
1726         if (this.well.length) {
1727             switch (this.well) {
1728                 case 'lg':
1729                 case 'sm':
1730                     cfg.cls +=' well well-' +this.well;
1731                     break;
1732                 default:
1733                     cfg.cls +=' well';
1734                     break;
1735             }
1736         }
1737         
1738         if (this.hidden) {
1739             cfg.cls += ' hidden';
1740         }
1741         
1742         
1743         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744             cfg.cls +=' alert alert-' + this.alert;
1745         }
1746         
1747         var body = cfg;
1748         
1749         if (this.panel.length) {
1750             cfg.cls += ' panel panel-' + this.panel;
1751             cfg.cn = [];
1752             if (this.header.length) {
1753                 
1754                 var h = [];
1755                 
1756                 if(this.expandable){
1757                     
1758                     cfg.cls = cfg.cls + ' expandable';
1759                     
1760                     h.push({
1761                         tag: 'i',
1762                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1763                     });
1764                     
1765                 }
1766                 
1767                 h.push(
1768                     {
1769                         tag: 'span',
1770                         cls : 'panel-title',
1771                         html : (this.expandable ? '&nbsp;' : '') + this.header
1772                     },
1773                     {
1774                         tag: 'span',
1775                         cls: 'panel-header-right',
1776                         html: this.rheader
1777                     }
1778                 );
1779                 
1780                 cfg.cn.push({
1781                     cls : 'panel-heading',
1782                     style : this.expandable ? 'cursor: pointer' : '',
1783                     cn : h
1784                 });
1785                 
1786             }
1787             
1788             body = false;
1789             cfg.cn.push({
1790                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1791                 html : this.html
1792             });
1793             
1794             
1795             if (this.footer.length) {
1796                 cfg.cn.push({
1797                     cls : 'panel-footer',
1798                     html : this.footer
1799                     
1800                 });
1801             }
1802             
1803         }
1804         
1805         if (body) {
1806             body.html = this.html || cfg.html;
1807             // prefix with the icons..
1808             if (this.fa) {
1809                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1810             }
1811             if (this.icon) {
1812                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1813             }
1814             
1815             
1816         }
1817         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818             cfg.cls =  'container';
1819         }
1820         
1821         return cfg;
1822     },
1823     
1824     initEvents: function() 
1825     {
1826         if(this.expandable){
1827             var headerEl = this.headerEl();
1828         
1829             if(headerEl){
1830                 headerEl.on('click', this.onToggleClick, this);
1831             }
1832         }
1833         
1834         if(this.clickable){
1835             this.el.on('click', this.onClick, this);
1836         }
1837         
1838     },
1839     
1840     onToggleClick : function()
1841     {
1842         var headerEl = this.headerEl();
1843         
1844         if(!headerEl){
1845             return;
1846         }
1847         
1848         if(this.expanded){
1849             this.collapse();
1850             return;
1851         }
1852         
1853         this.expand();
1854     },
1855     
1856     expand : function()
1857     {
1858         if(this.fireEvent('expand', this)) {
1859             
1860             this.expanded = true;
1861             
1862             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1863             
1864             this.el.select('.panel-body',true).first().removeClass('hide');
1865             
1866             var toggleEl = this.toggleEl();
1867
1868             if(!toggleEl){
1869                 return;
1870             }
1871
1872             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1873         }
1874         
1875     },
1876     
1877     collapse : function()
1878     {
1879         if(this.fireEvent('collapse', this)) {
1880             
1881             this.expanded = false;
1882             
1883             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884             this.el.select('.panel-body',true).first().addClass('hide');
1885         
1886             var toggleEl = this.toggleEl();
1887
1888             if(!toggleEl){
1889                 return;
1890             }
1891
1892             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1893         }
1894     },
1895     
1896     toggleEl : function()
1897     {
1898         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1899             return;
1900         }
1901         
1902         return this.el.select('.panel-heading .fa',true).first();
1903     },
1904     
1905     headerEl : function()
1906     {
1907         if(!this.el || !this.panel.length || !this.header.length){
1908             return;
1909         }
1910         
1911         return this.el.select('.panel-heading',true).first()
1912     },
1913     
1914     bodyEl : function()
1915     {
1916         if(!this.el || !this.panel.length){
1917             return;
1918         }
1919         
1920         return this.el.select('.panel-body',true).first()
1921     },
1922     
1923     titleEl : function()
1924     {
1925         if(!this.el || !this.panel.length || !this.header.length){
1926             return;
1927         }
1928         
1929         return this.el.select('.panel-title',true).first();
1930     },
1931     
1932     setTitle : function(v)
1933     {
1934         var titleEl = this.titleEl();
1935         
1936         if(!titleEl){
1937             return;
1938         }
1939         
1940         titleEl.dom.innerHTML = v;
1941     },
1942     
1943     getTitle : function()
1944     {
1945         
1946         var titleEl = this.titleEl();
1947         
1948         if(!titleEl){
1949             return '';
1950         }
1951         
1952         return titleEl.dom.innerHTML;
1953     },
1954     
1955     setRightTitle : function(v)
1956     {
1957         var t = this.el.select('.panel-header-right',true).first();
1958         
1959         if(!t){
1960             return;
1961         }
1962         
1963         t.dom.innerHTML = v;
1964     },
1965     
1966     onClick : function(e)
1967     {
1968         e.preventDefault();
1969         
1970         this.fireEvent('click', this, e);
1971     }
1972 });
1973
1974  /**
1975  * @class Roo.bootstrap.Card
1976  * @extends Roo.bootstrap.Component
1977  * @children Roo.bootstrap.Component
1978  * @licence LGPL
1979  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1980  *
1981  *
1982  * possible... may not be implemented..
1983  * @cfg {String} header_image  src url of image.
1984  * @cfg {String|Object} header
1985  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1987  * 
1988  * @cfg {String} title
1989  * @cfg {String} subtitle
1990  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991  * @cfg {String} footer
1992  
1993  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1994  * 
1995  * @cfg {String} margin (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2002  *
2003  * @cfg {String} padding (0|1|2|3|4|5)
2004  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006  * @cfg {String} padding_left (0|1|2|3|4|5)
2007  * @cfg {String} padding_right (0|1|2|3|4|5)
2008  * @cfg {String} padding_x (0|1|2|3|4|5)
2009  * @cfg {String} padding_y (0|1|2|3|4|5)
2010  *
2011  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  
2017  * @config {Boolean} dragable  if this card can be dragged.
2018  * @config {String} drag_group  group for drag
2019  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2020  * @config {String} drop_group  group for drag
2021  * 
2022  * @config {Boolean} collapsable can the body be collapsed.
2023  * @config {Boolean} collapsed is the body collapsed when rendered...
2024  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025  * @config {Boolean} rotated is the body rotated when rendered...
2026  * 
2027  * @constructor
2028  * Create a new Container
2029  * @param {Object} config The config object
2030  */
2031
2032 Roo.bootstrap.Card = function(config){
2033     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2034     
2035     this.addEvents({
2036          // raw events
2037         /**
2038          * @event drop
2039          * When a element a card is dropped
2040          * @param {Roo.bootstrap.Card} this
2041          *
2042          * 
2043          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044          * @param {String} position 'above' or 'below'
2045          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2046         
2047          */
2048         'drop' : true,
2049          /**
2050          * @event rotate
2051          * When a element a card is rotate
2052          * @param {Roo.bootstrap.Card} this
2053          * @param {Roo.Element} n the node being dropped?
2054          * @param {Boolean} rotate status
2055          */
2056         'rotate' : true,
2057         /**
2058          * @event cardover
2059          * When a card element is dragged over ready to drop (return false to block dropable)
2060          * @param {Roo.bootstrap.Card} this
2061          * @param {Object} data from dragdrop 
2062          */
2063          'cardover' : true
2064          
2065     });
2066 };
2067
2068
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2070     
2071     
2072     weight : '',
2073     
2074     margin: '', /// may be better in component?
2075     margin_top: '', 
2076     margin_bottom: '', 
2077     margin_left: '',
2078     margin_right: '',
2079     margin_x: '',
2080     margin_y: '',
2081     
2082     padding : '',
2083     padding_top: '', 
2084     padding_bottom: '', 
2085     padding_left: '',
2086     padding_right: '',
2087     padding_x: '',
2088     padding_y: '',
2089     
2090     display: '', 
2091     display_xs: '', 
2092     display_sm: '', 
2093     display_lg: '',
2094     display_xl: '',
2095  
2096     header_image  : '',
2097     header : '',
2098     header_size : 0,
2099     title : '',
2100     subtitle : '',
2101     html : '',
2102     footer: '',
2103
2104     collapsable : false,
2105     collapsed : false,
2106     rotateable : false,
2107     rotated : false,
2108     
2109     dragable : false,
2110     drag_group : false,
2111     dropable : false,
2112     drop_group : false,
2113     childContainer : false,
2114     dropEl : false, /// the dom placeholde element that indicates drop location.
2115     containerEl: false, // body container
2116     bodyEl: false, // card-body
2117     headerContainerEl : false, //
2118     headerEl : false,
2119     header_imageEl : false,
2120     
2121     
2122     layoutCls : function()
2123     {
2124         var cls = '';
2125         var t = this;
2126         Roo.log(this.margin_bottom.length);
2127         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2129             
2130             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2132             }
2133             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2135             }
2136         });
2137         
2138         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2141             }
2142         });
2143         
2144         // more generic support?
2145         if (this.hidden) {
2146             cls += ' d-none';
2147         }
2148         
2149         return cls;
2150     },
2151  
2152        // Roo.log("Call onRender: " + this.xtype);
2153         /*  We are looking at something like this.
2154 <div class="card">
2155     <img src="..." class="card-img-top" alt="...">
2156     <div class="card-body">
2157         <h5 class="card-title">Card title</h5>
2158          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2159
2160         >> this bit is really the body...
2161         <div> << we will ad dthis in hopefully it will not break shit.
2162         
2163         ** card text does not actually have any styling...
2164         
2165             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2166         
2167         </div> <<
2168           <a href="#" class="card-link">Card link</a>
2169           
2170     </div>
2171     <div class="card-footer">
2172         <small class="text-muted">Last updated 3 mins ago</small>
2173     </div>
2174 </div>
2175          */
2176     getAutoCreate : function(){
2177         
2178         var cfg = {
2179             tag : 'div',
2180             cls : 'card',
2181             cn : [ ]
2182         };
2183         
2184         if (this.weight.length && this.weight != 'light') {
2185             cfg.cls += ' text-white';
2186         } else {
2187             cfg.cls += ' text-dark'; // need as it's nested..
2188         }
2189         if (this.weight.length) {
2190             cfg.cls += ' bg-' + this.weight;
2191         }
2192         
2193         cfg.cls += ' ' + this.layoutCls(); 
2194         
2195         var hdr = false;
2196         var hdr_ctr = false;
2197         if (this.header.length) {
2198             hdr = {
2199                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2201                 cn : []
2202             };
2203             cfg.cn.push(hdr);
2204             hdr_ctr = hdr;
2205         } else {
2206             hdr = {
2207                 tag : 'div',
2208                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2209                 cn : []
2210             };
2211             cfg.cn.push(hdr);
2212             hdr_ctr = hdr;
2213         }
2214         if (this.collapsable) {
2215             hdr_ctr = {
2216             tag : 'a',
2217             cls : 'd-block user-select-none',
2218             cn: [
2219                     {
2220                         tag: 'i',
2221                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2222                     }
2223                    
2224                 ]
2225             };
2226             hdr.cn.push(hdr_ctr);
2227         }
2228         
2229         hdr_ctr.cn.push(        {
2230             tag: 'span',
2231             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2232             html : this.header
2233         });
2234         
2235         
2236         if (this.header_image.length) {
2237             cfg.cn.push({
2238                 tag : 'img',
2239                 cls : 'card-img-top',
2240                 src: this.header_image // escape?
2241             });
2242         } else {
2243             cfg.cn.push({
2244                     tag : 'div',
2245                     cls : 'card-img-top d-none' 
2246                 });
2247         }
2248             
2249         var body = {
2250             tag : 'div',
2251             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2252             cn : []
2253         };
2254         var obody = body;
2255         if (this.collapsable || this.rotateable) {
2256             obody = {
2257                 tag: 'div',
2258                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2259                 cn : [  body ]
2260             };
2261         }
2262         
2263         cfg.cn.push(obody);
2264         
2265         if (this.title.length) {
2266             body.cn.push({
2267                 tag : 'div',
2268                 cls : 'card-title',
2269                 src: this.title // escape?
2270             });
2271         }  
2272         
2273         if (this.subtitle.length) {
2274             body.cn.push({
2275                 tag : 'div',
2276                 cls : 'card-title',
2277                 src: this.subtitle // escape?
2278             });
2279         }
2280         
2281         body.cn.push({
2282             tag : 'div',
2283             cls : 'roo-card-body-ctr'
2284         });
2285         
2286         if (this.html.length) {
2287             body.cn.push({
2288                 tag: 'div',
2289                 html : this.html
2290             });
2291         }
2292         // fixme ? handle objects?
2293         
2294         if (this.footer.length) {
2295            
2296             cfg.cn.push({
2297                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2298                 html : this.footer
2299             });
2300             
2301         } else {
2302             cfg.cn.push({cls : 'card-footer d-none'});
2303         }
2304         
2305         // footer...
2306         
2307         return cfg;
2308     },
2309     
2310     
2311     getCardHeader : function()
2312     {
2313         var  ret = this.el.select('.card-header',true).first();
2314         if (ret.hasClass('d-none')) {
2315             ret.removeClass('d-none');
2316         }
2317         
2318         return ret;
2319     },
2320     getCardFooter : function()
2321     {
2322         var  ret = this.el.select('.card-footer',true).first();
2323         if (ret.hasClass('d-none')) {
2324             ret.removeClass('d-none');
2325         }
2326         
2327         return ret;
2328     },
2329     getCardImageTop : function()
2330     {
2331         var  ret = this.header_imageEl;
2332         if (ret.hasClass('d-none')) {
2333             ret.removeClass('d-none');
2334         }
2335             
2336         return ret;
2337     },
2338     
2339     getChildContainer : function()
2340     {
2341         
2342         if(!this.el){
2343             return false;
2344         }
2345         return this.el.select('.roo-card-body-ctr',true).first();    
2346     },
2347     
2348     initEvents: function() 
2349     {
2350         this.bodyEl = this.el.select('.card-body',true).first(); 
2351         this.containerEl = this.getChildContainer();
2352         if(this.dragable){
2353             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354                     containerScroll: true,
2355                     ddGroup: this.drag_group || 'default_card_drag_group'
2356             });
2357             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2358         }
2359         if (this.dropable) {
2360             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361                 containerScroll: true,
2362                 ddGroup: this.drop_group || 'default_card_drag_group'
2363             });
2364             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369         }
2370         
2371         if (this.collapsable) {
2372             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2373         }
2374         if (this.rotateable) {
2375             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2376         }
2377         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2378          
2379         this.footerEl = this.el.select('.card-footer',true).first();
2380         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382         this.headerEl = this.el.select('.card-header',true).first();
2383         
2384         if (this.rotated) {
2385             this.el.addClass('roo-card-rotated');
2386             this.fireEvent('rotate', this, true);
2387         }
2388         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2389         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390         
2391     },
2392     getDragData : function(e)
2393     {
2394         var target = this.getEl();
2395         if (target) {
2396             //this.handleSelection(e);
2397             
2398             var dragData = {
2399                 source: this,
2400                 copy: false,
2401                 nodes: this.getEl(),
2402                 records: []
2403             };
2404             
2405             
2406             dragData.ddel = target.dom ;    // the div element
2407             Roo.log(target.getWidth( ));
2408             dragData.ddel.style.width = target.getWidth() + 'px';
2409             
2410             return dragData;
2411         }
2412         return false;
2413     },
2414     /**
2415     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2416     *    whole Element becomes the target, and this causes the drop gesture to append.
2417     *
2418     *    Returns an object:
2419     *     {
2420            
2421            position : 'below' or 'above'
2422            card  : relateive to card OBJECT (or true for no cards listed)
2423            items_n : relative to nth item in list
2424            card_n : relative to  nth card in list
2425     }
2426     *
2427     *    
2428     */
2429     getTargetFromEvent : function(e, dragged_card_el)
2430     {
2431         var target = e.getTarget();
2432         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433             target = target.parentNode;
2434         }
2435         
2436         var ret = {
2437             position: '',
2438             cards : [],
2439             card_n : -1,
2440             items_n : -1,
2441             card : false 
2442         };
2443         
2444         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445         // see if target is one of the 'cards'...
2446         
2447         
2448         //Roo.log(this.items.length);
2449         var pos = false;
2450         
2451         var last_card_n = 0;
2452         var cards_len  = 0;
2453         for (var i = 0;i< this.items.length;i++) {
2454             
2455             if (!this.items[i].el.hasClass('card')) {
2456                  continue;
2457             }
2458             pos = this.getDropPoint(e, this.items[i].el.dom);
2459             
2460             cards_len = ret.cards.length;
2461             //Roo.log(this.items[i].el.dom.id);
2462             ret.cards.push(this.items[i]);
2463             last_card_n  = i;
2464             if (ret.card_n < 0 && pos == 'above') {
2465                 ret.position = cards_len > 0 ? 'below' : pos;
2466                 ret.items_n = i > 0 ? i - 1 : 0;
2467                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2468                 ret.card = ret.cards[ret.card_n];
2469             }
2470         }
2471         if (!ret.cards.length) {
2472             ret.card = true;
2473             ret.position = 'below';
2474             ret.items_n;
2475             return ret;
2476         }
2477         // could not find a card.. stick it at the end..
2478         if (ret.card_n < 0) {
2479             ret.card_n = last_card_n;
2480             ret.card = ret.cards[last_card_n];
2481             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482             ret.position = 'below';
2483         }
2484         
2485         if (this.items[ret.items_n].el == dragged_card_el) {
2486             return false;
2487         }
2488         
2489         if (ret.position == 'below') {
2490             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2491             
2492             if (card_after  && card_after.el == dragged_card_el) {
2493                 return false;
2494             }
2495             return ret;
2496         }
2497         
2498         // its's after ..
2499         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2500         
2501         if (card_before  && card_before.el == dragged_card_el) {
2502             return false;
2503         }
2504         
2505         return ret;
2506     },
2507     
2508     onNodeEnter : function(n, dd, e, data){
2509         return false;
2510     },
2511     onNodeOver : function(n, dd, e, data)
2512     {
2513        
2514         var target_info = this.getTargetFromEvent(e,data.source.el);
2515         if (target_info === false) {
2516             this.dropPlaceHolder('hide');
2517             return false;
2518         }
2519         Roo.log(['getTargetFromEvent', target_info ]);
2520         
2521         
2522         if (this.fireEvent('cardover', this, [ data ]) === false) {
2523             return false;
2524         }
2525         
2526         this.dropPlaceHolder('show', target_info,data);
2527         
2528         return false; 
2529     },
2530     onNodeOut : function(n, dd, e, data){
2531         this.dropPlaceHolder('hide');
2532      
2533     },
2534     onNodeDrop : function(n, dd, e, data)
2535     {
2536         
2537         // call drop - return false if
2538         
2539         // this could actually fail - if the Network drops..
2540         // we will ignore this at present..- client should probably reload
2541         // the whole set of cards if stuff like that fails.
2542         
2543         
2544         var info = this.getTargetFromEvent(e,data.source.el);
2545         if (info === false) {
2546             return false;
2547         }
2548         this.dropPlaceHolder('hide');
2549   
2550           
2551     
2552         this.acceptCard(data.source, info.position, info.card, info.items_n);
2553         return true;
2554          
2555     },
2556     firstChildCard : function()
2557     {
2558         for (var i = 0;i< this.items.length;i++) {
2559             
2560             if (!this.items[i].el.hasClass('card')) {
2561                  continue;
2562             }
2563             return this.items[i];
2564         }
2565         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2566     },
2567     /**
2568      * accept card
2569      *
2570      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2571      */
2572     acceptCard : function(move_card,  position, next_to_card )
2573     {
2574         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2575             return false;
2576         }
2577         
2578         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2579         
2580         move_card.parent().removeCard(move_card);
2581         
2582         
2583         var dom = move_card.el.dom;
2584         dom.style.width = ''; // clear with - which is set by drag.
2585         
2586         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587             var cardel = next_to_card.el.dom;
2588             
2589             if (position == 'above' ) {
2590                 cardel.parentNode.insertBefore(dom, cardel);
2591             } else if (cardel.nextSibling) {
2592                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2593             } else {
2594                 cardel.parentNode.append(dom);
2595             }
2596         } else {
2597             // card container???
2598             this.containerEl.dom.append(dom);
2599         }
2600         
2601         //FIXME HANDLE card = true 
2602         
2603         // add this to the correct place in items.
2604         
2605         // remove Card from items.
2606         
2607        
2608         if (this.items.length) {
2609             var nitems = [];
2610             //Roo.log([info.items_n, info.position, this.items.length]);
2611             for (var i =0; i < this.items.length; i++) {
2612                 if (i == to_items_n && position == 'above') {
2613                     nitems.push(move_card);
2614                 }
2615                 nitems.push(this.items[i]);
2616                 if (i == to_items_n && position == 'below') {
2617                     nitems.push(move_card);
2618                 }
2619             }
2620             this.items = nitems;
2621             Roo.log(this.items);
2622         } else {
2623             this.items.push(move_card);
2624         }
2625         
2626         move_card.parentId = this.id;
2627         
2628         return true;
2629         
2630         
2631     },
2632     removeCard : function(c)
2633     {
2634         this.items = this.items.filter(function(e) { return e != c });
2635  
2636         var dom = c.el.dom;
2637         dom.parentNode.removeChild(dom);
2638         dom.style.width = ''; // clear with - which is set by drag.
2639         c.parentId = false;
2640         
2641     },
2642     
2643     /**    Decide whether to drop above or below a View node. */
2644     getDropPoint : function(e, n, dd)
2645     {
2646         if (dd) {
2647              return false;
2648         }
2649         if (n == this.containerEl.dom) {
2650             return "above";
2651         }
2652         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653         var c = t + (b - t) / 2;
2654         var y = Roo.lib.Event.getPageY(e);
2655         if(y <= c) {
2656             return "above";
2657         }else{
2658             return "below";
2659         }
2660     },
2661     onToggleCollapse : function(e)
2662         {
2663         if (this.collapsed) {
2664             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665             this.collapsableEl.addClass('show');
2666             this.collapsed = false;
2667             return;
2668         }
2669         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670         this.collapsableEl.removeClass('show');
2671         this.collapsed = true;
2672         
2673     
2674     },
2675     
2676     onToggleRotate : function(e)
2677     {
2678         this.collapsableEl.removeClass('show');
2679         this.footerEl.removeClass('d-none');
2680         this.el.removeClass('roo-card-rotated');
2681         this.el.removeClass('d-none');
2682         if (this.rotated) {
2683             
2684             this.collapsableEl.addClass('show');
2685             this.rotated = false;
2686             this.fireEvent('rotate', this, this.rotated);
2687             return;
2688         }
2689         this.el.addClass('roo-card-rotated');
2690         this.footerEl.addClass('d-none');
2691         this.el.select('.roo-collapsable').removeClass('show');
2692         
2693         this.rotated = true;
2694         this.fireEvent('rotate', this, this.rotated);
2695     
2696     },
2697     
2698     dropPlaceHolder: function (action, info, data)
2699     {
2700         if (this.dropEl === false) {
2701             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2702             cls : 'd-none'
2703             },true);
2704         }
2705         this.dropEl.removeClass(['d-none', 'd-block']);        
2706         if (action == 'hide') {
2707             
2708             this.dropEl.addClass('d-none');
2709             return;
2710         }
2711         // FIXME - info.card == true!!!
2712         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2713         
2714         if (info.card !== true) {
2715             var cardel = info.card.el.dom;
2716             
2717             if (info.position == 'above') {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719             } else if (cardel.nextSibling) {
2720                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2721             } else {
2722                 cardel.parentNode.append(this.dropEl.dom);
2723             }
2724         } else {
2725             // card container???
2726             this.containerEl.dom.append(this.dropEl.dom);
2727         }
2728         
2729         this.dropEl.addClass('d-block roo-card-dropzone');
2730         
2731         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2732         
2733         
2734     
2735     
2736     
2737     },
2738     setHeaderText: function(html)
2739     {
2740         this.header = html;
2741         if (this.headerContainerEl) {
2742             this.headerContainerEl.dom.innerHTML = html;
2743         }
2744     },
2745     onHeaderImageLoad : function(ev, he)
2746     {
2747         if (!this.header_image_fit_square) {
2748             return;
2749         }
2750         
2751         var hw = he.naturalHeight / he.naturalWidth;
2752         // wide image = < 0
2753         // tall image = > 1
2754         //var w = he.dom.naturalWidth;
2755         var ww = he.width;
2756         he.style.left =  0;
2757         he.style.position =  'relative';
2758         if (hw > 1) {
2759             var nw = (ww * (1/hw));
2760             Roo.get(he).setSize( ww * (1/hw),  ww);
2761             he.style.left =  ((ww - nw)/ 2) + 'px';
2762             he.style.position =  'relative';
2763         }
2764
2765     }
2766
2767     
2768 });
2769
2770 /*
2771  * - LGPL
2772  *
2773  * Card header - holder for the card header elements.
2774  * 
2775  */
2776
2777 /**
2778  * @class Roo.bootstrap.CardHeader
2779  * @extends Roo.bootstrap.Element
2780  * @parent Roo.bootstrap.Card
2781  * @children Roo.bootstrap.Component
2782  * Bootstrap CardHeader class
2783  * @constructor
2784  * Create a new Card Header - that you can embed children into
2785  * @param {Object} config The config object
2786  */
2787
2788 Roo.bootstrap.CardHeader = function(config){
2789     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2790 };
2791
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2793     
2794     
2795     container_method : 'getCardHeader' 
2796     
2797      
2798     
2799     
2800    
2801 });
2802
2803  
2804
2805  /*
2806  * - LGPL
2807  *
2808  * Card footer - holder for the card footer elements.
2809  * 
2810  */
2811
2812 /**
2813  * @class Roo.bootstrap.CardFooter
2814  * @extends Roo.bootstrap.Element
2815  * @parent Roo.bootstrap.Card
2816  * @children Roo.bootstrap.Component
2817  * Bootstrap CardFooter class
2818  * 
2819  * @constructor
2820  * Create a new Card Footer - that you can embed children into
2821  * @param {Object} config The config object
2822  */
2823
2824 Roo.bootstrap.CardFooter = function(config){
2825     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2826 };
2827
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2829     
2830     
2831     container_method : 'getCardFooter' 
2832     
2833      
2834     
2835     
2836    
2837 });
2838
2839  
2840
2841  /*
2842  * - LGPL
2843  *
2844  * Card header - holder for the card header elements.
2845  * 
2846  */
2847
2848 /**
2849  * @class Roo.bootstrap.CardImageTop
2850  * @extends Roo.bootstrap.Element
2851  * @parent Roo.bootstrap.Card
2852  * @children Roo.bootstrap.Component
2853  * Bootstrap CardImageTop class
2854  * 
2855  * @constructor
2856  * Create a new Card Image Top container
2857  * @param {Object} config The config object
2858  */
2859
2860 Roo.bootstrap.CardImageTop = function(config){
2861     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2862 };
2863
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2865     
2866    
2867     container_method : 'getCardImageTop' 
2868     
2869      
2870     
2871    
2872 });
2873
2874  
2875
2876  
2877 /*
2878 * Licence: LGPL
2879 */
2880
2881 /**
2882  * @class Roo.bootstrap.ButtonUploader
2883  * @extends Roo.bootstrap.Button
2884  * Bootstrap Button Uploader class - it's a button which when you add files to it
2885  *
2886  * 
2887  * @cfg {Number} errorTimeout default 3000
2888  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2889  * @cfg {Array}  html The button text.
2890  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2891  *
2892  * @constructor
2893  * Create a new CardUploader
2894  * @param {Object} config The config object
2895  */
2896
2897 Roo.bootstrap.ButtonUploader = function(config){
2898     
2899  
2900     
2901     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2902     
2903      
2904      this.addEvents({
2905          // raw events
2906         /**
2907          * @event beforeselect
2908          * When button is pressed, before show upload files dialog is shown
2909          * @param {Roo.bootstrap.UploaderButton} this
2910          *
2911          */
2912         'beforeselect' : true,
2913          /**
2914          * @event fired when files have been selected, 
2915          * When a the download link is clicked
2916          * @param {Roo.bootstrap.UploaderButton} this
2917          * @param {Array} Array of files that have been uploaded
2918          */
2919         'uploaded' : true
2920         
2921     });
2922 };
2923  
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2925     
2926      
2927     errorTimeout : 3000,
2928      
2929     images : false,
2930    
2931     fileCollection : false,
2932     allowBlank : true,
2933     
2934     multiple : true,
2935     
2936     getAutoCreate : function()
2937     {
2938         var im = {
2939             tag: 'input',
2940             type : 'file',
2941             cls : 'd-none  roo-card-upload-selector' 
2942           
2943         };
2944         if (this.multiple) {
2945             im.multiple = 'multiple';
2946         }
2947         
2948         return  {
2949             cls :'div' ,
2950             cn : [
2951                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2952                 im
2953
2954             ]
2955         };
2956            
2957          
2958     },
2959      
2960    
2961     initEvents : function()
2962     {
2963         
2964         Roo.bootstrap.Button.prototype.initEvents.call(this);
2965         
2966         
2967         
2968         
2969         
2970         this.urlAPI = (window.createObjectURL && window) || 
2971                                 (window.URL && URL.revokeObjectURL && URL) || 
2972                                 (window.webkitURL && webkitURL);
2973                         
2974          
2975          
2976          
2977         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2978         
2979         this.selectorEl.on('change', this.onFileSelected, this);
2980          
2981          
2982        
2983     },
2984     
2985    
2986     onClick : function(e)
2987     {
2988         e.preventDefault();
2989         
2990         if ( this.fireEvent('beforeselect', this) === false) {
2991             return;
2992         }
2993          
2994         this.selectorEl.dom.click();
2995          
2996     },
2997     
2998     onFileSelected : function(e)
2999     {
3000         e.preventDefault();
3001         
3002         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3003             return;
3004         }
3005         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3006         this.selectorEl.dom.value  = '';// hopefully reset..
3007         
3008         this.fireEvent('uploaded', this,  files );
3009         
3010     },
3011     
3012        
3013    
3014     
3015     /**
3016      * addCard - add an Attachment to the uploader
3017      * @param data - the data about the image to upload
3018      *
3019      * {
3020           id : 123
3021           title : "Title of file",
3022           is_uploaded : false,
3023           src : "http://.....",
3024           srcfile : { the File upload object },
3025           mimetype : file.type,
3026           preview : false,
3027           is_deleted : 0
3028           .. any other data...
3029         }
3030      *
3031      * 
3032     */
3033      
3034     reset: function()
3035     {
3036          
3037          this.selectorEl
3038     } 
3039     
3040     
3041     
3042     
3043 });
3044  /*
3045  * - LGPL
3046  *
3047  * image
3048  * 
3049  */
3050
3051
3052 /**
3053  * @class Roo.bootstrap.Img
3054  * @extends Roo.bootstrap.Component
3055  * Bootstrap Img class
3056  * @cfg {Boolean} imgResponsive false | true
3057  * @cfg {String} border rounded | circle | thumbnail
3058  * @cfg {String} src image source
3059  * @cfg {String} alt image alternative text
3060  * @cfg {String} href a tag href
3061  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3062  * @cfg {String} xsUrl xs image source
3063  * @cfg {String} smUrl sm image source
3064  * @cfg {String} mdUrl md image source
3065  * @cfg {String} lgUrl lg image source
3066  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3067  * 
3068  * @constructor
3069  * Create a new Input
3070  * @param {Object} config The config object
3071  */
3072
3073 Roo.bootstrap.Img = function(config){
3074     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3075     
3076     this.addEvents({
3077         // img events
3078         /**
3079          * @event click
3080          * The img click event for the img.
3081          * @param {Roo.EventObject} e
3082          */
3083         "click" : true,
3084         /**
3085          * @event load
3086          * The when any image loads
3087          * @param {Roo.EventObject} e
3088          */
3089         "load" : true
3090     });
3091 };
3092
3093 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3094     
3095     imgResponsive: true,
3096     border: '',
3097     src: 'about:blank',
3098     href: false,
3099     target: false,
3100     xsUrl: '',
3101     smUrl: '',
3102     mdUrl: '',
3103     lgUrl: '',
3104     backgroundContain : false,
3105
3106     getAutoCreate : function()
3107     {   
3108         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3109             return this.createSingleImg();
3110         }
3111         
3112         var cfg = {
3113             tag: 'div',
3114             cls: 'roo-image-responsive-group',
3115             cn: []
3116         };
3117         var _this = this;
3118         
3119         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3120             
3121             if(!_this[size + 'Url']){
3122                 return;
3123             }
3124             
3125             var img = {
3126                 tag: 'img',
3127                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3128                 html: _this.html || cfg.html,
3129                 src: _this[size + 'Url']
3130             };
3131             
3132             img.cls += ' roo-image-responsive-' + size;
3133             
3134             var s = ['xs', 'sm', 'md', 'lg'];
3135             
3136             s.splice(s.indexOf(size), 1);
3137             
3138             Roo.each(s, function(ss){
3139                 img.cls += ' hidden-' + ss;
3140             });
3141             
3142             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3143                 cfg.cls += ' img-' + _this.border;
3144             }
3145             
3146             if(_this.alt){
3147                 cfg.alt = _this.alt;
3148             }
3149             
3150             if(_this.href){
3151                 var a = {
3152                     tag: 'a',
3153                     href: _this.href,
3154                     cn: [
3155                         img
3156                     ]
3157                 };
3158
3159                 if(this.target){
3160                     a.target = _this.target;
3161                 }
3162             }
3163             
3164             cfg.cn.push((_this.href) ? a : img);
3165             
3166         });
3167         
3168         return cfg;
3169     },
3170     
3171     createSingleImg : function()
3172     {
3173         var cfg = {
3174             tag: 'img',
3175             cls: (this.imgResponsive) ? 'img-responsive' : '',
3176             html : null,
3177             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3178         };
3179         
3180         if (this.backgroundContain) {
3181             cfg.cls += ' background-contain';
3182         }
3183         
3184         cfg.html = this.html || cfg.html;
3185         
3186         if (this.backgroundContain) {
3187             cfg.style="background-image: url(" + this.src + ')';
3188         } else {
3189             cfg.src = this.src || cfg.src;
3190         }
3191         
3192         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3193             cfg.cls += ' img-' + this.border;
3194         }
3195         
3196         if(this.alt){
3197             cfg.alt = this.alt;
3198         }
3199         
3200         if(this.href){
3201             var a = {
3202                 tag: 'a',
3203                 href: this.href,
3204                 cn: [
3205                     cfg
3206                 ]
3207             };
3208             
3209             if(this.target){
3210                 a.target = this.target;
3211             }
3212             
3213         }
3214         
3215         return (this.href) ? a : cfg;
3216     },
3217     
3218     initEvents: function() 
3219     {
3220         if(!this.href){
3221             this.el.on('click', this.onClick, this);
3222         }
3223         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3224             this.el.on('load', this.onImageLoad, this);
3225         } else {
3226             // not sure if this works.. not tested
3227             this.el.select('img', true).on('load', this.onImageLoad, this);
3228         }
3229         
3230     },
3231     
3232     onClick : function(e)
3233     {
3234         Roo.log('img onclick');
3235         this.fireEvent('click', this, e);
3236     },
3237     onImageLoad: function(e)
3238     {
3239         Roo.log('img load');
3240         this.fireEvent('load', this, e);
3241     },
3242     
3243     /**
3244      * Sets the url of the image - used to update it
3245      * @param {String} url the url of the image
3246      */
3247     
3248     setSrc : function(url)
3249     {
3250         this.src =  url;
3251         
3252         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3253             if (this.backgroundContain) {
3254                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3255             } else {
3256                 this.el.dom.src =  url;
3257             }
3258             return;
3259         }
3260         
3261         this.el.select('img', true).first().dom.src =  url;
3262     }
3263     
3264     
3265    
3266 });
3267
3268  /*
3269  * - LGPL
3270  *
3271  * image
3272  * 
3273  */
3274
3275
3276 /**
3277  * @class Roo.bootstrap.Link
3278  * @extends Roo.bootstrap.Component
3279  * @children Roo.bootstrap.Component
3280  * Bootstrap Link Class (eg. '<a href>')
3281  
3282  * @cfg {String} alt image alternative text
3283  * @cfg {String} href a tag href
3284  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3285  * @cfg {String} html the content of the link.
3286  * @cfg {String} anchor name for the anchor link
3287  * @cfg {String} fa - favicon
3288
3289  * @cfg {Boolean} preventDefault (true | false) default false
3290
3291  * 
3292  * @constructor
3293  * Create a new Input
3294  * @param {Object} config The config object
3295  */
3296
3297 Roo.bootstrap.Link = function(config){
3298     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3299     
3300     this.addEvents({
3301         // img events
3302         /**
3303          * @event click
3304          * The img click event for the img.
3305          * @param {Roo.EventObject} e
3306          */
3307         "click" : true
3308     });
3309 };
3310
3311 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3312     
3313     href: false,
3314     target: false,
3315     preventDefault: false,
3316     anchor : false,
3317     alt : false,
3318     fa: false,
3319
3320
3321     getAutoCreate : function()
3322     {
3323         var html = this.html || '';
3324         
3325         if (this.fa !== false) {
3326             html = '<i class="fa fa-' + this.fa + '"></i>';
3327         }
3328         var cfg = {
3329             tag: 'a'
3330         };
3331         // anchor's do not require html/href...
3332         if (this.anchor === false) {
3333             cfg.html = html;
3334             cfg.href = this.href || '#';
3335         } else {
3336             cfg.name = this.anchor;
3337             if (this.html !== false || this.fa !== false) {
3338                 cfg.html = html;
3339             }
3340             if (this.href !== false) {
3341                 cfg.href = this.href;
3342             }
3343         }
3344         
3345         if(this.alt !== false){
3346             cfg.alt = this.alt;
3347         }
3348         
3349         
3350         if(this.target !== false) {
3351             cfg.target = this.target;
3352         }
3353         
3354         return cfg;
3355     },
3356     
3357     initEvents: function() {
3358         
3359         if(!this.href || this.preventDefault){
3360             this.el.on('click', this.onClick, this);
3361         }
3362     },
3363     
3364     onClick : function(e)
3365     {
3366         if(this.preventDefault){
3367             e.preventDefault();
3368         }
3369         //Roo.log('img onclick');
3370         this.fireEvent('click', this, e);
3371     }
3372    
3373 });
3374
3375  /*
3376  * - LGPL
3377  *
3378  * header
3379  * 
3380  */
3381
3382 /**
3383  * @class Roo.bootstrap.Header
3384  * @extends Roo.bootstrap.Component
3385  * @children Roo.bootstrap.Component
3386  * Bootstrap Header class
3387  *
3388  * 
3389  * @cfg {String} html content of header
3390  * @cfg {Number} level (1|2|3|4|5|6) default 1
3391  * 
3392  * @constructor
3393  * Create a new Header
3394  * @param {Object} config The config object
3395  */
3396
3397
3398 Roo.bootstrap.Header  = function(config){
3399     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3400 };
3401
3402 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3403     
3404     //href : false,
3405     html : false,
3406     level : 1,
3407     
3408     
3409     
3410     getAutoCreate : function(){
3411         
3412         
3413         
3414         var cfg = {
3415             tag: 'h' + (1 *this.level),
3416             html: this.html || ''
3417         } ;
3418         
3419         return cfg;
3420     }
3421    
3422 });
3423
3424  
3425
3426  /**
3427  * @class Roo.bootstrap.MenuMgr
3428  * @licence LGPL
3429  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3430  * @static
3431  */
3432 Roo.bootstrap.menu.Manager = function(){
3433    var menus, active, groups = {}, attached = false, lastShow = new Date();
3434
3435    // private - called when first menu is created
3436    function init(){
3437        menus = {};
3438        active = new Roo.util.MixedCollection();
3439        Roo.get(document).addKeyListener(27, function(){
3440            if(active.length > 0){
3441                hideAll();
3442            }
3443        });
3444    }
3445
3446    // private
3447    function hideAll(){
3448        if(active && active.length > 0){
3449            var c = active.clone();
3450            c.each(function(m){
3451                m.hide();
3452            });
3453        }
3454    }
3455
3456    // private
3457    function onHide(m){
3458        active.remove(m);
3459        if(active.length < 1){
3460            Roo.get(document).un("mouseup", onMouseDown);
3461             
3462            attached = false;
3463        }
3464    }
3465
3466    // private
3467    function onShow(m){
3468        var last = active.last();
3469        lastShow = new Date();
3470        active.add(m);
3471        if(!attached){
3472           Roo.get(document).on("mouseup", onMouseDown);
3473            
3474            attached = true;
3475        }
3476        if(m.parentMenu){
3477           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3478           m.parentMenu.activeChild = m;
3479        }else if(last && last.isVisible()){
3480           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3481        }
3482    }
3483
3484    // private
3485    function onBeforeHide(m){
3486        if(m.activeChild){
3487            m.activeChild.hide();
3488        }
3489        if(m.autoHideTimer){
3490            clearTimeout(m.autoHideTimer);
3491            delete m.autoHideTimer;
3492        }
3493    }
3494
3495    // private
3496    function onBeforeShow(m){
3497        var pm = m.parentMenu;
3498        if(!pm && !m.allowOtherMenus){
3499            hideAll();
3500        }else if(pm && pm.activeChild && active != m){
3501            pm.activeChild.hide();
3502        }
3503    }
3504
3505    // private this should really trigger on mouseup..
3506    function onMouseDown(e){
3507         Roo.log("on Mouse Up");
3508         
3509         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3510             Roo.log("MenuManager hideAll");
3511             hideAll();
3512             e.stopEvent();
3513         }
3514         
3515         
3516    }
3517
3518    // private
3519    function onBeforeCheck(mi, state){
3520        if(state){
3521            var g = groups[mi.group];
3522            for(var i = 0, l = g.length; i < l; i++){
3523                if(g[i] != mi){
3524                    g[i].setChecked(false);
3525                }
3526            }
3527        }
3528    }
3529
3530    return {
3531
3532        /**
3533         * Hides all menus that are currently visible
3534         */
3535        hideAll : function(){
3536             hideAll();  
3537        },
3538
3539        // private
3540        register : function(menu){
3541            if(!menus){
3542                init();
3543            }
3544            menus[menu.id] = menu;
3545            menu.on("beforehide", onBeforeHide);
3546            menu.on("hide", onHide);
3547            menu.on("beforeshow", onBeforeShow);
3548            menu.on("show", onShow);
3549            var g = menu.group;
3550            if(g && menu.events["checkchange"]){
3551                if(!groups[g]){
3552                    groups[g] = [];
3553                }
3554                groups[g].push(menu);
3555                menu.on("checkchange", onCheck);
3556            }
3557        },
3558
3559         /**
3560          * Returns a {@link Roo.menu.Menu} object
3561          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3562          * be used to generate and return a new Menu instance.
3563          */
3564        get : function(menu){
3565            if(typeof menu == "string"){ // menu id
3566                return menus[menu];
3567            }else if(menu.events){  // menu instance
3568                return menu;
3569            }
3570            /*else if(typeof menu.length == 'number'){ // array of menu items?
3571                return new Roo.bootstrap.Menu({items:menu});
3572            }else{ // otherwise, must be a config
3573                return new Roo.bootstrap.Menu(menu);
3574            }
3575            */
3576            return false;
3577        },
3578
3579        // private
3580        unregister : function(menu){
3581            delete menus[menu.id];
3582            menu.un("beforehide", onBeforeHide);
3583            menu.un("hide", onHide);
3584            menu.un("beforeshow", onBeforeShow);
3585            menu.un("show", onShow);
3586            var g = menu.group;
3587            if(g && menu.events["checkchange"]){
3588                groups[g].remove(menu);
3589                menu.un("checkchange", onCheck);
3590            }
3591        },
3592
3593        // private
3594        registerCheckable : function(menuItem){
3595            var g = menuItem.group;
3596            if(g){
3597                if(!groups[g]){
3598                    groups[g] = [];
3599                }
3600                groups[g].push(menuItem);
3601                menuItem.on("beforecheckchange", onBeforeCheck);
3602            }
3603        },
3604
3605        // private
3606        unregisterCheckable : function(menuItem){
3607            var g = menuItem.group;
3608            if(g){
3609                groups[g].remove(menuItem);
3610                menuItem.un("beforecheckchange", onBeforeCheck);
3611            }
3612        }
3613    };
3614 }(); 
3615 /**
3616  * @class Roo.bootstrap.menu.Menu
3617  * @extends Roo.bootstrap.Component
3618  * @licence LGPL
3619  * @children Roo.bootstrap.menu.Item
3620  * @parent none
3621  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3622  * 
3623  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3624  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3625  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3626  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3627   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3628   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3629  
3630  * @constructor
3631  * Create a new Menu
3632  * @param {Object} config The config objectQ
3633  */
3634
3635
3636 Roo.bootstrap.menu.Menu = function(config){
3637     
3638     if (config.type == 'treeview') {
3639         // normally menu's are drawn attached to the document to handle layering etc..
3640         // however treeview (used by the docs menu is drawn into the parent element)
3641         this.container_method = 'getChildContainer'; 
3642     }
3643     
3644     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3645     if (this.registerMenu && this.type != 'treeview')  {
3646         Roo.bootstrap.menu.Manager.register(this);
3647     }
3648     
3649     
3650     this.addEvents({
3651         /**
3652          * @event beforeshow
3653          * Fires before this menu is displayed (return false to block)
3654          * @param {Roo.menu.Menu} this
3655          */
3656         beforeshow : true,
3657         /**
3658          * @event beforehide
3659          * Fires before this menu is hidden (return false to block)
3660          * @param {Roo.menu.Menu} this
3661          */
3662         beforehide : true,
3663         /**
3664          * @event show
3665          * Fires after this menu is displayed
3666          * @param {Roo.menu.Menu} this
3667          */
3668         show : true,
3669         /**
3670          * @event hide
3671          * Fires after this menu is hidden
3672          * @param {Roo.menu.Menu} this
3673          */
3674         hide : true,
3675         /**
3676          * @event click
3677          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3678          * @param {Roo.menu.Menu} this
3679          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680          * @param {Roo.EventObject} e
3681          */
3682         click : true,
3683         /**
3684          * @event mouseover
3685          * Fires when the mouse is hovering over this menu
3686          * @param {Roo.menu.Menu} this
3687          * @param {Roo.EventObject} e
3688          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3689          */
3690         mouseover : true,
3691         /**
3692          * @event mouseout
3693          * Fires when the mouse exits this menu
3694          * @param {Roo.menu.Menu} this
3695          * @param {Roo.EventObject} e
3696          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3697          */
3698         mouseout : true,
3699         /**
3700          * @event itemclick
3701          * Fires when a menu item contained in this menu is clicked
3702          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3703          * @param {Roo.EventObject} e
3704          */
3705         itemclick: true
3706     });
3707     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3708 };
3709
3710 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3711     
3712    /// html : false,
3713    
3714     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3715     type: false,
3716     /**
3717      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3718      */
3719     registerMenu : true,
3720     
3721     menuItems :false, // stores the menu items..
3722     
3723     hidden:true,
3724         
3725     parentMenu : false,
3726     
3727     stopEvent : true,
3728     
3729     isLink : false,
3730     
3731     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3732     
3733     hideTrigger : false,
3734     
3735     align : 'tl-bl?',
3736     
3737     
3738     getChildContainer : function() {
3739         return this.el;  
3740     },
3741     
3742     getAutoCreate : function(){
3743          
3744         //if (['right'].indexOf(this.align)!==-1) {
3745         //    cfg.cn[1].cls += ' pull-right'
3746         //}
3747          
3748         var cfg = {
3749             tag : 'ul',
3750             cls : 'dropdown-menu shadow' ,
3751             style : 'z-index:1000'
3752             
3753         };
3754         
3755         if (this.type === 'submenu') {
3756             cfg.cls = 'submenu active';
3757         }
3758         if (this.type === 'treeview') {
3759             cfg.cls = 'treeview-menu';
3760         }
3761         
3762         return cfg;
3763     },
3764     initEvents : function() {
3765         
3766        // Roo.log("ADD event");
3767        // Roo.log(this.triggerEl.dom);
3768         if (this.triggerEl) {
3769             
3770             this.triggerEl.on('click', this.onTriggerClick, this);
3771             
3772             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3773             
3774             if (!this.hideTrigger) {
3775                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3776                     // dropdown toggle on the 'a' in BS4?
3777                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3778                 } else {
3779                     this.triggerEl.addClass('dropdown-toggle');
3780                 }
3781             }
3782         }
3783         
3784         if (Roo.isTouch) {
3785             this.el.on('touchstart'  , this.onTouch, this);
3786         }
3787         this.el.on('click' , this.onClick, this);
3788
3789         this.el.on("mouseover", this.onMouseOver, this);
3790         this.el.on("mouseout", this.onMouseOut, this);
3791         
3792     },
3793     
3794     findTargetItem : function(e)
3795     {
3796         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3797         if(!t){
3798             return false;
3799         }
3800         //Roo.log(t);         Roo.log(t.id);
3801         if(t && t.id){
3802             //Roo.log(this.menuitems);
3803             return this.menuitems.get(t.id);
3804             
3805             //return this.items.get(t.menuItemId);
3806         }
3807         
3808         return false;
3809     },
3810     
3811     onTouch : function(e) 
3812     {
3813         Roo.log("menu.onTouch");
3814         //e.stopEvent(); this make the user popdown broken
3815         this.onClick(e);
3816     },
3817     
3818     onClick : function(e)
3819     {
3820         Roo.log("menu.onClick");
3821         
3822         var t = this.findTargetItem(e);
3823         if(!t || t.isContainer){
3824             return;
3825         }
3826         Roo.log(e);
3827         /*
3828         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3829             if(t == this.activeItem && t.shouldDeactivate(e)){
3830                 this.activeItem.deactivate();
3831                 delete this.activeItem;
3832                 return;
3833             }
3834             if(t.canActivate){
3835                 this.setActiveItem(t, true);
3836             }
3837             return;
3838             
3839             
3840         }
3841         */
3842        
3843         Roo.log('pass click event');
3844         
3845         t.onClick(e);
3846         
3847         this.fireEvent("click", this, t, e);
3848         
3849         var _this = this;
3850         
3851         if(!t.href.length || t.href == '#'){
3852             (function() { _this.hide(); }).defer(100);
3853         }
3854         
3855     },
3856     
3857     onMouseOver : function(e){
3858         var t  = this.findTargetItem(e);
3859         //Roo.log(t);
3860         //if(t){
3861         //    if(t.canActivate && !t.disabled){
3862         //        this.setActiveItem(t, true);
3863         //    }
3864         //}
3865         
3866         this.fireEvent("mouseover", this, e, t);
3867     },
3868     isVisible : function(){
3869         return !this.hidden;
3870     },
3871     onMouseOut : function(e){
3872         var t  = this.findTargetItem(e);
3873         
3874         //if(t ){
3875         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3876         //        this.activeItem.deactivate();
3877         //        delete this.activeItem;
3878         //    }
3879         //}
3880         this.fireEvent("mouseout", this, e, t);
3881     },
3882     
3883     
3884     /**
3885      * Displays this menu relative to another element
3886      * @param {String/HTMLElement/Roo.Element} element The element to align to
3887      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3888      * the element (defaults to this.defaultAlign)
3889      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3890      */
3891     show : function(el, pos, parentMenu)
3892     {
3893         if (false === this.fireEvent("beforeshow", this)) {
3894             Roo.log("show canceled");
3895             return;
3896         }
3897         this.parentMenu = parentMenu;
3898         if(!this.el){
3899             this.render();
3900         }
3901         this.el.addClass('show'); // show otherwise we do not know how big we are..
3902          
3903         var xy = this.el.getAlignToXY(el, pos);
3904         
3905         // bl-tl << left align  below
3906         // tl-bl << left align 
3907         
3908         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3909             // if it goes to far to the right.. -> align left.
3910             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3911         }
3912         if(xy[0] < 0){
3913             // was left align - go right?
3914             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3915         }
3916         
3917         // goes down the bottom
3918         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3919            xy[1]  < 0 ){
3920             var a = this.align.replace('?', '').split('-');
3921             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3922             
3923         }
3924         
3925         this.showAt(  xy , parentMenu, false);
3926     },
3927      /**
3928      * Displays this menu at a specific xy position
3929      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3930      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3931      */
3932     showAt : function(xy, parentMenu, /* private: */_e){
3933         this.parentMenu = parentMenu;
3934         if(!this.el){
3935             this.render();
3936         }
3937         if(_e !== false){
3938             this.fireEvent("beforeshow", this);
3939             //xy = this.el.adjustForConstraints(xy);
3940         }
3941         
3942         //this.el.show();
3943         this.hideMenuItems();
3944         this.hidden = false;
3945         if (this.triggerEl) {
3946             this.triggerEl.addClass('open');
3947         }
3948         
3949         this.el.addClass('show');
3950         
3951         
3952         
3953         // reassign x when hitting right
3954         
3955         // reassign y when hitting bottom
3956         
3957         // but the list may align on trigger left or trigger top... should it be a properity?
3958         
3959         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3960             this.el.setXY(xy);
3961         }
3962         
3963         this.focus();
3964         this.fireEvent("show", this);
3965     },
3966     
3967     focus : function(){
3968         return;
3969         if(!this.hidden){
3970             this.doFocus.defer(50, this);
3971         }
3972     },
3973
3974     doFocus : function(){
3975         if(!this.hidden){
3976             this.focusEl.focus();
3977         }
3978     },
3979
3980     /**
3981      * Hides this menu and optionally all parent menus
3982      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3983      */
3984     hide : function(deep)
3985     {
3986         if (false === this.fireEvent("beforehide", this)) {
3987             Roo.log("hide canceled");
3988             return;
3989         }
3990         this.hideMenuItems();
3991         if(this.el && this.isVisible()){
3992            
3993             if(this.activeItem){
3994                 this.activeItem.deactivate();
3995                 this.activeItem = null;
3996             }
3997             if (this.triggerEl) {
3998                 this.triggerEl.removeClass('open');
3999             }
4000             
4001             this.el.removeClass('show');
4002             this.hidden = true;
4003             this.fireEvent("hide", this);
4004         }
4005         if(deep === true && this.parentMenu){
4006             this.parentMenu.hide(true);
4007         }
4008     },
4009     
4010     onTriggerClick : function(e)
4011     {
4012         Roo.log('trigger click');
4013         
4014         var target = e.getTarget();
4015         
4016         Roo.log(target.nodeName.toLowerCase());
4017         
4018         if(target.nodeName.toLowerCase() === 'i'){
4019             e.preventDefault();
4020         }
4021         
4022     },
4023     
4024     onTriggerPress  : function(e)
4025     {
4026         Roo.log('trigger press');
4027         //Roo.log(e.getTarget());
4028        // Roo.log(this.triggerEl.dom);
4029        
4030         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4031         var pel = Roo.get(e.getTarget());
4032         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4033             Roo.log('is treeview or dropdown?');
4034             return;
4035         }
4036         
4037         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4038             return;
4039         }
4040         
4041         if (this.isVisible()) {
4042             Roo.log('hide');
4043             this.hide();
4044         } else {
4045             Roo.log('show');
4046             
4047             this.show(this.triggerEl, this.align, false);
4048         }
4049         
4050         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4051             e.stopEvent();
4052         }
4053         
4054     },
4055        
4056     
4057     hideMenuItems : function()
4058     {
4059         Roo.log("hide Menu Items");
4060         if (!this.el) { 
4061             return;
4062         }
4063         
4064         this.el.select('.open',true).each(function(aa) {
4065             
4066             aa.removeClass('open');
4067          
4068         });
4069     },
4070     addxtypeChild : function (tree, cntr) {
4071         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4072           
4073         this.menuitems.add(comp);
4074         return comp;
4075
4076     },
4077     getEl : function()
4078     {
4079         Roo.log(this.el);
4080         return this.el;
4081     },
4082     
4083     clear : function()
4084     {
4085         this.getEl().dom.innerHTML = '';
4086         this.menuitems.clear();
4087     }
4088 });
4089
4090  
4091  /**
4092  * @class Roo.bootstrap.menu.Item
4093  * @extends Roo.bootstrap.Component
4094  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4095  * @parent Roo.bootstrap.menu.Menu
4096  * @licence LGPL
4097  * Bootstrap MenuItem class
4098  * 
4099  * @cfg {String} html the menu label
4100  * @cfg {String} href the link
4101  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4102  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4103  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4104  * @cfg {String} fa favicon to show on left of menu item.
4105  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4106  * 
4107  * 
4108  * @constructor
4109  * Create a new MenuItem
4110  * @param {Object} config The config object
4111  */
4112
4113
4114 Roo.bootstrap.menu.Item = function(config){
4115     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4116     this.addEvents({
4117         // raw events
4118         /**
4119          * @event click
4120          * The raw click event for the entire grid.
4121          * @param {Roo.bootstrap.menu.Item} this
4122          * @param {Roo.EventObject} e
4123          */
4124         "click" : true
4125     });
4126 };
4127
4128 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4129     
4130     href : false,
4131     html : false,
4132     preventDefault: false,
4133     isContainer : false,
4134     active : false,
4135     fa: false,
4136     
4137     getAutoCreate : function(){
4138         
4139         if(this.isContainer){
4140             return {
4141                 tag: 'li',
4142                 cls: 'dropdown-menu-item '
4143             };
4144         }
4145         var ctag = {
4146             tag: 'span',
4147             html: 'Link'
4148         };
4149         
4150         var anc = {
4151             tag : 'a',
4152             cls : 'dropdown-item',
4153             href : '#',
4154             cn : [  ]
4155         };
4156         
4157         if (this.fa !== false) {
4158             anc.cn.push({
4159                 tag : 'i',
4160                 cls : 'fa fa-' + this.fa
4161             });
4162         }
4163         
4164         anc.cn.push(ctag);
4165         
4166         
4167         var cfg= {
4168             tag: 'li',
4169             cls: 'dropdown-menu-item',
4170             cn: [ anc ]
4171         };
4172         if (this.parent().type == 'treeview') {
4173             cfg.cls = 'treeview-menu';
4174         }
4175         if (this.active) {
4176             cfg.cls += ' active';
4177         }
4178         
4179         
4180         
4181         anc.href = this.href || cfg.cn[0].href ;
4182         ctag.html = this.html || cfg.cn[0].html ;
4183         return cfg;
4184     },
4185     
4186     initEvents: function()
4187     {
4188         if (this.parent().type == 'treeview') {
4189             this.el.select('a').on('click', this.onClick, this);
4190         }
4191         
4192         if (this.menu) {
4193             this.menu.parentType = this.xtype;
4194             this.menu.triggerEl = this.el;
4195             this.menu = this.addxtype(Roo.apply({}, this.menu));
4196         }
4197         
4198     },
4199     onClick : function(e)
4200     {
4201         Roo.log('item on click ');
4202         
4203         if(this.preventDefault){
4204             e.preventDefault();
4205         }
4206         //this.parent().hideMenuItems();
4207         
4208         this.fireEvent('click', this, e);
4209     },
4210     getEl : function()
4211     {
4212         return this.el;
4213     } 
4214 });
4215
4216  
4217
4218  
4219
4220   
4221 /**
4222  * @class Roo.bootstrap.menu.Separator
4223  * @extends Roo.bootstrap.Component
4224  * @licence LGPL
4225  * @parent Roo.bootstrap.menu.Menu
4226  * Bootstrap Separator class
4227  * 
4228  * @constructor
4229  * Create a new Separator
4230  * @param {Object} config The config object
4231  */
4232
4233
4234 Roo.bootstrap.menu.Separator = function(config){
4235     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4236 };
4237
4238 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4239     
4240     getAutoCreate : function(){
4241         var cfg = {
4242             tag : 'li',
4243             cls: 'dropdown-divider divider'
4244         };
4245         
4246         return cfg;
4247     }
4248    
4249 });
4250
4251  
4252
4253  
4254 /*
4255 * Licence: LGPL
4256 */
4257
4258 /**
4259  * @class Roo.bootstrap.Modal
4260  * @extends Roo.bootstrap.Component
4261  * @parent none builder
4262  * @children Roo.bootstrap.Component
4263  * Bootstrap Modal class
4264  * @cfg {String} title Title of dialog
4265  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4266  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4267  * @cfg {Boolean} specificTitle default false
4268  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4269  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4270  * @cfg {Boolean} animate default true
4271  * @cfg {Boolean} allow_close default true
4272  * @cfg {Boolean} fitwindow default false
4273  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4274  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4275  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4276  * @cfg {String} size (sm|lg|xl) default empty
4277  * @cfg {Number} max_width set the max width of modal
4278  * @cfg {Boolean} editableTitle can the title be edited
4279
4280  *
4281  *
4282  * @constructor
4283  * Create a new Modal Dialog
4284  * @param {Object} config The config object
4285  */
4286
4287 Roo.bootstrap.Modal = function(config){
4288     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4289     this.addEvents({
4290         // raw events
4291         /**
4292          * @event btnclick
4293          * The raw btnclick event for the button
4294          * @param {Roo.EventObject} e
4295          */
4296         "btnclick" : true,
4297         /**
4298          * @event resize
4299          * Fire when dialog resize
4300          * @param {Roo.bootstrap.Modal} this
4301          * @param {Roo.EventObject} e
4302          */
4303         "resize" : true,
4304         /**
4305          * @event titlechanged
4306          * Fire when the editable title has been changed
4307          * @param {Roo.bootstrap.Modal} this
4308          * @param {Roo.EventObject} value
4309          */
4310         "titlechanged" : true 
4311         
4312     });
4313     this.buttons = this.buttons || [];
4314
4315     if (this.tmpl) {
4316         this.tmpl = Roo.factory(this.tmpl);
4317     }
4318
4319 };
4320
4321 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4322
4323     title : 'test dialog',
4324
4325     buttons : false,
4326
4327     // set on load...
4328
4329     html: false,
4330
4331     tmp: false,
4332
4333     specificTitle: false,
4334
4335     buttonPosition: 'right',
4336
4337     allow_close : true,
4338
4339     animate : true,
4340
4341     fitwindow: false,
4342     
4343      // private
4344     dialogEl: false,
4345     bodyEl:  false,
4346     footerEl:  false,
4347     titleEl:  false,
4348     closeEl:  false,
4349
4350     size: '',
4351     
4352     max_width: 0,
4353     
4354     max_height: 0,
4355     
4356     fit_content: false,
4357     editableTitle  : false,
4358
4359     onRender : function(ct, position)
4360     {
4361         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4362
4363         if(!this.el){
4364             var cfg = Roo.apply({},  this.getAutoCreate());
4365             cfg.id = Roo.id();
4366             //if(!cfg.name){
4367             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4368             //}
4369             //if (!cfg.name.length) {
4370             //    delete cfg.name;
4371            // }
4372             if (this.cls) {
4373                 cfg.cls += ' ' + this.cls;
4374             }
4375             if (this.style) {
4376                 cfg.style = this.style;
4377             }
4378             this.el = Roo.get(document.body).createChild(cfg, position);
4379         }
4380         //var type = this.el.dom.type;
4381
4382
4383         if(this.tabIndex !== undefined){
4384             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4385         }
4386
4387         this.dialogEl = this.el.select('.modal-dialog',true).first();
4388         this.bodyEl = this.el.select('.modal-body',true).first();
4389         this.closeEl = this.el.select('.modal-header .close', true).first();
4390         this.headerEl = this.el.select('.modal-header',true).first();
4391         this.titleEl = this.el.select('.modal-title',true).first();
4392         this.footerEl = this.el.select('.modal-footer',true).first();
4393
4394         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4395         
4396         //this.el.addClass("x-dlg-modal");
4397
4398         if (this.buttons.length) {
4399             Roo.each(this.buttons, function(bb) {
4400                 var b = Roo.apply({}, bb);
4401                 b.xns = b.xns || Roo.bootstrap;
4402                 b.xtype = b.xtype || 'Button';
4403                 if (typeof(b.listeners) == 'undefined') {
4404                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4405                 }
4406
4407                 var btn = Roo.factory(b);
4408
4409                 btn.render(this.getButtonContainer());
4410
4411             },this);
4412         }
4413         // render the children.
4414         var nitems = [];
4415
4416         if(typeof(this.items) != 'undefined'){
4417             var items = this.items;
4418             delete this.items;
4419
4420             for(var i =0;i < items.length;i++) {
4421                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4422             }
4423         }
4424
4425         this.items = nitems;
4426
4427         // where are these used - they used to be body/close/footer
4428
4429
4430         this.initEvents();
4431         //this.el.addClass([this.fieldClass, this.cls]);
4432
4433     },
4434
4435     getAutoCreate : function()
4436     {
4437         // we will default to modal-body-overflow - might need to remove or make optional later.
4438         var bdy = {
4439                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4440                 html : this.html || ''
4441         };
4442
4443         var title = {
4444             tag: 'h5',
4445             cls : 'modal-title',
4446             html : this.title
4447         };
4448
4449         if(this.specificTitle){ // WTF is this?
4450             title = this.title;
4451         }
4452
4453         var header = [];
4454         if (this.allow_close && Roo.bootstrap.version == 3) {
4455             header.push({
4456                 tag: 'button',
4457                 cls : 'close',
4458                 html : '&times'
4459             });
4460         }
4461
4462         header.push(title);
4463
4464         if (this.editableTitle) {
4465             header.push({
4466                 cls: 'form-control roo-editable-title d-none',
4467                 tag: 'input',
4468                 type: 'text'
4469             });
4470         }
4471         
4472         if (this.allow_close && Roo.bootstrap.version == 4) {
4473             header.push({
4474                 tag: 'button',
4475                 cls : 'close',
4476                 html : '&times'
4477             });
4478         }
4479         
4480         var size = '';
4481
4482         if(this.size.length){
4483             size = 'modal-' + this.size;
4484         }
4485         
4486         var footer = Roo.bootstrap.version == 3 ?
4487             {
4488                 cls : 'modal-footer',
4489                 cn : [
4490                     {
4491                         tag: 'div',
4492                         cls: 'btn-' + this.buttonPosition
4493                     }
4494                 ]
4495
4496             } :
4497             {  // BS4 uses mr-auto on left buttons....
4498                 cls : 'modal-footer'
4499             };
4500
4501             
4502
4503         
4504         
4505         var modal = {
4506             cls: "modal",
4507              cn : [
4508                 {
4509                     cls: "modal-dialog " + size,
4510                     cn : [
4511                         {
4512                             cls : "modal-content",
4513                             cn : [
4514                                 {
4515                                     cls : 'modal-header',
4516                                     cn : header
4517                                 },
4518                                 bdy,
4519                                 footer
4520                             ]
4521
4522                         }
4523                     ]
4524
4525                 }
4526             ]
4527         };
4528
4529         if(this.animate){
4530             modal.cls += ' fade';
4531         }
4532
4533         return modal;
4534
4535     },
4536     getChildContainer : function() {
4537
4538          return this.bodyEl;
4539
4540     },
4541     getButtonContainer : function() {
4542         
4543          return Roo.bootstrap.version == 4 ?
4544             this.el.select('.modal-footer',true).first()
4545             : this.el.select('.modal-footer div',true).first();
4546
4547     },
4548     initEvents : function()
4549     {
4550         if (this.allow_close) {
4551             this.closeEl.on('click', this.hide, this);
4552         }
4553         Roo.EventManager.onWindowResize(this.resize, this, true);
4554         if (this.editableTitle) {
4555             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4556             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557             this.headerEditEl.on('keyup', function(e) {
4558                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559                         this.toggleHeaderInput(false)
4560                     }
4561                 }, this);
4562             this.headerEditEl.on('blur', function(e) {
4563                 this.toggleHeaderInput(false)
4564             },this);
4565         }
4566
4567     },
4568   
4569
4570     resize : function()
4571     {
4572         this.maskEl.setSize(
4573             Roo.lib.Dom.getViewWidth(true),
4574             Roo.lib.Dom.getViewHeight(true)
4575         );
4576         
4577         if (this.fitwindow) {
4578             
4579            this.dialogEl.setStyle( { 'max-width' : '100%' });
4580             this.setSize(
4581                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4583             );
4584             return;
4585         }
4586         
4587         if(this.max_width !== 0) {
4588             
4589             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4590             
4591             if(this.height) {
4592                 this.setSize(w, this.height);
4593                 return;
4594             }
4595             
4596             if(this.max_height) {
4597                 this.setSize(w,Math.min(
4598                     this.max_height,
4599                     Roo.lib.Dom.getViewportHeight(true) - 60
4600                 ));
4601                 
4602                 return;
4603             }
4604             
4605             if(!this.fit_content) {
4606                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4607                 return;
4608             }
4609             
4610             this.setSize(w, Math.min(
4611                 60 +
4612                 this.headerEl.getHeight() + 
4613                 this.footerEl.getHeight() + 
4614                 this.getChildHeight(this.bodyEl.dom.childNodes),
4615                 Roo.lib.Dom.getViewportHeight(true) - 60)
4616             );
4617         }
4618         
4619     },
4620
4621     setSize : function(w,h)
4622     {
4623         if (!w && !h) {
4624             return;
4625         }
4626         
4627         this.resizeTo(w,h);
4628     },
4629
4630     show : function() {
4631
4632         if (!this.rendered) {
4633             this.render();
4634         }
4635         this.toggleHeaderInput(false);
4636         //this.el.setStyle('display', 'block');
4637         this.el.removeClass('hideing');
4638         this.el.dom.style.display='block';
4639         
4640         Roo.get(document.body).addClass('modal-open');
4641  
4642         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4643             
4644             (function(){
4645                 this.el.addClass('show');
4646                 this.el.addClass('in');
4647             }).defer(50, this);
4648         }else{
4649             this.el.addClass('show');
4650             this.el.addClass('in');
4651         }
4652
4653         // not sure how we can show data in here..
4654         //if (this.tmpl) {
4655         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4656         //}
4657
4658         Roo.get(document.body).addClass("x-body-masked");
4659         
4660         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4661         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4662         this.maskEl.dom.style.display = 'block';
4663         this.maskEl.addClass('show');
4664         
4665         
4666         this.resize();
4667         
4668         this.fireEvent('show', this);
4669
4670         // set zindex here - otherwise it appears to be ignored...
4671         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4672
4673         (function () {
4674             this.items.forEach( function(e) {
4675                 e.layout ? e.layout() : false;
4676
4677             });
4678         }).defer(100,this);
4679
4680     },
4681     hide : function()
4682     {
4683         if(this.fireEvent("beforehide", this) !== false){
4684             
4685             this.maskEl.removeClass('show');
4686             
4687             this.maskEl.dom.style.display = '';
4688             Roo.get(document.body).removeClass("x-body-masked");
4689             this.el.removeClass('in');
4690             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4691
4692             if(this.animate){ // why
4693                 this.el.addClass('hideing');
4694                 this.el.removeClass('show');
4695                 (function(){
4696                     if (!this.el.hasClass('hideing')) {
4697                         return; // it's been shown again...
4698                     }
4699                     
4700                     this.el.dom.style.display='';
4701
4702                     Roo.get(document.body).removeClass('modal-open');
4703                     this.el.removeClass('hideing');
4704                 }).defer(150,this);
4705                 
4706             }else{
4707                 this.el.removeClass('show');
4708                 this.el.dom.style.display='';
4709                 Roo.get(document.body).removeClass('modal-open');
4710
4711             }
4712             this.fireEvent('hide', this);
4713         }
4714     },
4715     isVisible : function()
4716     {
4717         
4718         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4719         
4720     },
4721
4722     addButton : function(str, cb)
4723     {
4724
4725
4726         var b = Roo.apply({}, { html : str } );
4727         b.xns = b.xns || Roo.bootstrap;
4728         b.xtype = b.xtype || 'Button';
4729         if (typeof(b.listeners) == 'undefined') {
4730             b.listeners = { click : cb.createDelegate(this)  };
4731         }
4732
4733         var btn = Roo.factory(b);
4734
4735         btn.render(this.getButtonContainer());
4736
4737         return btn;
4738
4739     },
4740
4741     setDefaultButton : function(btn)
4742     {
4743         //this.el.select('.modal-footer').()
4744     },
4745
4746     resizeTo: function(w,h)
4747     {
4748         this.dialogEl.setWidth(w);
4749         
4750         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4751
4752         this.bodyEl.setHeight(h - diff);
4753         
4754         this.fireEvent('resize', this);
4755     },
4756     
4757     setContentSize  : function(w, h)
4758     {
4759
4760     },
4761     onButtonClick: function(btn,e)
4762     {
4763         //Roo.log([a,b,c]);
4764         this.fireEvent('btnclick', btn.name, e);
4765     },
4766      /**
4767      * Set the title of the Dialog
4768      * @param {String} str new Title
4769      */
4770     setTitle: function(str) {
4771         this.titleEl.dom.innerHTML = str;
4772         this.title = str;
4773     },
4774     /**
4775      * Set the body of the Dialog
4776      * @param {String} str new Title
4777      */
4778     setBody: function(str) {
4779         this.bodyEl.dom.innerHTML = str;
4780     },
4781     /**
4782      * Set the body of the Dialog using the template
4783      * @param {Obj} data - apply this data to the template and replace the body contents.
4784      */
4785     applyBody: function(obj)
4786     {
4787         if (!this.tmpl) {
4788             Roo.log("Error - using apply Body without a template");
4789             //code
4790         }
4791         this.tmpl.overwrite(this.bodyEl, obj);
4792     },
4793     
4794     getChildHeight : function(child_nodes)
4795     {
4796         if(
4797             !child_nodes ||
4798             child_nodes.length == 0
4799         ) {
4800             return 0;
4801         }
4802         
4803         var child_height = 0;
4804         
4805         for(var i = 0; i < child_nodes.length; i++) {
4806             
4807             /*
4808             * for modal with tabs...
4809             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4810                 
4811                 var layout_childs = child_nodes[i].childNodes;
4812                 
4813                 for(var j = 0; j < layout_childs.length; j++) {
4814                     
4815                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4816                         
4817                         var layout_body_childs = layout_childs[j].childNodes;
4818                         
4819                         for(var k = 0; k < layout_body_childs.length; k++) {
4820                             
4821                             if(layout_body_childs[k].classList.contains('navbar')) {
4822                                 child_height += layout_body_childs[k].offsetHeight;
4823                                 continue;
4824                             }
4825                             
4826                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4827                                 
4828                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4829                                 
4830                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4831                                     
4832                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4833                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4834                                         continue;
4835                                     }
4836                                     
4837                                 }
4838                                 
4839                             }
4840                             
4841                         }
4842                     }
4843                 }
4844                 continue;
4845             }
4846             */
4847             
4848             child_height += child_nodes[i].offsetHeight;
4849             // Roo.log(child_nodes[i].offsetHeight);
4850         }
4851         
4852         return child_height;
4853     },
4854     toggleHeaderInput : function(is_edit)
4855     {
4856         if (!this.editableTitle) {
4857             return; // not editable.
4858         }
4859         if (is_edit && this.is_header_editing) {
4860             return; // already editing..
4861         }
4862         if (is_edit) {
4863     
4864             this.headerEditEl.dom.value = this.title;
4865             this.headerEditEl.removeClass('d-none');
4866             this.headerEditEl.dom.focus();
4867             this.titleEl.addClass('d-none');
4868             
4869             this.is_header_editing = true;
4870             return
4871         }
4872         // flip back to not editing.
4873         this.title = this.headerEditEl.dom.value;
4874         this.headerEditEl.addClass('d-none');
4875         this.titleEl.removeClass('d-none');
4876         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4877         this.is_header_editing = false;
4878         this.fireEvent('titlechanged', this, this.title);
4879     
4880             
4881         
4882     }
4883
4884 });
4885
4886
4887 Roo.apply(Roo.bootstrap.Modal,  {
4888     /**
4889          * Button config that displays a single OK button
4890          * @type Object
4891          */
4892         OK :  [{
4893             name : 'ok',
4894             weight : 'primary',
4895             html : 'OK'
4896         }],
4897         /**
4898          * Button config that displays Yes and No buttons
4899          * @type Object
4900          */
4901         YESNO : [
4902             {
4903                 name  : 'no',
4904                 html : 'No'
4905             },
4906             {
4907                 name  :'yes',
4908                 weight : 'primary',
4909                 html : 'Yes'
4910             }
4911         ],
4912
4913         /**
4914          * Button config that displays OK and Cancel buttons
4915          * @type Object
4916          */
4917         OKCANCEL : [
4918             {
4919                name : 'cancel',
4920                 html : 'Cancel'
4921             },
4922             {
4923                 name : 'ok',
4924                 weight : 'primary',
4925                 html : 'OK'
4926             }
4927         ],
4928         /**
4929          * Button config that displays Yes, No and Cancel buttons
4930          * @type Object
4931          */
4932         YESNOCANCEL : [
4933             {
4934                 name : 'yes',
4935                 weight : 'primary',
4936                 html : 'Yes'
4937             },
4938             {
4939                 name : 'no',
4940                 html : 'No'
4941             },
4942             {
4943                 name : 'cancel',
4944                 html : 'Cancel'
4945             }
4946         ],
4947         
4948         zIndex : 10001
4949 });
4950
4951 /*
4952  * - LGPL
4953  *
4954  * messagebox - can be used as a replace
4955  * 
4956  */
4957 /**
4958  * @class Roo.MessageBox
4959  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4960  * Example usage:
4961  *<pre><code>
4962 // Basic alert:
4963 Roo.Msg.alert('Status', 'Changes saved successfully.');
4964
4965 // Prompt for user data:
4966 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4967     if (btn == 'ok'){
4968         // process text value...
4969     }
4970 });
4971
4972 // Show a dialog using config options:
4973 Roo.Msg.show({
4974    title:'Save Changes?',
4975    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4976    buttons: Roo.Msg.YESNOCANCEL,
4977    fn: processResult,
4978    animEl: 'elId'
4979 });
4980 </code></pre>
4981  * @static
4982  */
4983 Roo.bootstrap.MessageBox = function(){
4984     var dlg, opt, mask, waitTimer;
4985     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4986     var buttons, activeTextEl, bwidth;
4987
4988     
4989     // private
4990     var handleButton = function(button){
4991         dlg.hide();
4992         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4993     };
4994
4995     // private
4996     var handleHide = function(){
4997         if(opt && opt.cls){
4998             dlg.el.removeClass(opt.cls);
4999         }
5000         //if(waitTimer){
5001         //    Roo.TaskMgr.stop(waitTimer);
5002         //    waitTimer = null;
5003         //}
5004     };
5005
5006     // private
5007     var updateButtons = function(b){
5008         var width = 0;
5009         if(!b){
5010             buttons["ok"].hide();
5011             buttons["cancel"].hide();
5012             buttons["yes"].hide();
5013             buttons["no"].hide();
5014             dlg.footerEl.hide();
5015             
5016             return width;
5017         }
5018         dlg.footerEl.show();
5019         for(var k in buttons){
5020             if(typeof buttons[k] != "function"){
5021                 if(b[k]){
5022                     buttons[k].show();
5023                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5024                     width += buttons[k].el.getWidth()+15;
5025                 }else{
5026                     buttons[k].hide();
5027                 }
5028             }
5029         }
5030         return width;
5031     };
5032
5033     // private
5034     var handleEsc = function(d, k, e){
5035         if(opt && opt.closable !== false){
5036             dlg.hide();
5037         }
5038         if(e){
5039             e.stopEvent();
5040         }
5041     };
5042
5043     return {
5044         /**
5045          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5046          * @return {Roo.BasicDialog} The BasicDialog element
5047          */
5048         getDialog : function(){
5049            if(!dlg){
5050                 dlg = new Roo.bootstrap.Modal( {
5051                     //draggable: true,
5052                     //resizable:false,
5053                     //constraintoviewport:false,
5054                     //fixedcenter:true,
5055                     //collapsible : false,
5056                     //shim:true,
5057                     //modal: true,
5058                 //    width: 'auto',
5059                   //  height:100,
5060                     //buttonAlign:"center",
5061                     closeClick : function(){
5062                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5063                             handleButton("no");
5064                         }else{
5065                             handleButton("cancel");
5066                         }
5067                     }
5068                 });
5069                 dlg.render();
5070                 dlg.on("hide", handleHide);
5071                 mask = dlg.mask;
5072                 //dlg.addKeyListener(27, handleEsc);
5073                 buttons = {};
5074                 this.buttons = buttons;
5075                 var bt = this.buttonText;
5076                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5077                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5078                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5079                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5080                 //Roo.log(buttons);
5081                 bodyEl = dlg.bodyEl.createChild({
5082
5083                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5084                         '<textarea class="roo-mb-textarea"></textarea>' +
5085                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5086                 });
5087                 msgEl = bodyEl.dom.firstChild;
5088                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5089                 textboxEl.enableDisplayMode();
5090                 textboxEl.addKeyListener([10,13], function(){
5091                     if(dlg.isVisible() && opt && opt.buttons){
5092                         if(opt.buttons.ok){
5093                             handleButton("ok");
5094                         }else if(opt.buttons.yes){
5095                             handleButton("yes");
5096                         }
5097                     }
5098                 });
5099                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5100                 textareaEl.enableDisplayMode();
5101                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5102                 progressEl.enableDisplayMode();
5103                 
5104                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5105                 var pf = progressEl.dom.firstChild;
5106                 if (pf) {
5107                     pp = Roo.get(pf.firstChild);
5108                     pp.setHeight(pf.offsetHeight);
5109                 }
5110                 
5111             }
5112             return dlg;
5113         },
5114
5115         /**
5116          * Updates the message box body text
5117          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5118          * the XHTML-compliant non-breaking space character '&amp;#160;')
5119          * @return {Roo.MessageBox} This message box
5120          */
5121         updateText : function(text)
5122         {
5123             if(!dlg.isVisible() && !opt.width){
5124                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5125                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5126             }
5127             msgEl.innerHTML = text || '&#160;';
5128       
5129             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5130             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5131             var w = Math.max(
5132                     Math.min(opt.width || cw , this.maxWidth), 
5133                     Math.max(opt.minWidth || this.minWidth, bwidth)
5134             );
5135             if(opt.prompt){
5136                 activeTextEl.setWidth(w);
5137             }
5138             if(dlg.isVisible()){
5139                 dlg.fixedcenter = false;
5140             }
5141             // to big, make it scroll. = But as usual stupid IE does not support
5142             // !important..
5143             
5144             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5145                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5146                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5147             } else {
5148                 bodyEl.dom.style.height = '';
5149                 bodyEl.dom.style.overflowY = '';
5150             }
5151             if (cw > w) {
5152                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5153             } else {
5154                 bodyEl.dom.style.overflowX = '';
5155             }
5156             
5157             dlg.setContentSize(w, bodyEl.getHeight());
5158             if(dlg.isVisible()){
5159                 dlg.fixedcenter = true;
5160             }
5161             return this;
5162         },
5163
5164         /**
5165          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5166          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5167          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5168          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5169          * @return {Roo.MessageBox} This message box
5170          */
5171         updateProgress : function(value, text){
5172             if(text){
5173                 this.updateText(text);
5174             }
5175             
5176             if (pp) { // weird bug on my firefox - for some reason this is not defined
5177                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5178                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5179             }
5180             return this;
5181         },        
5182
5183         /**
5184          * Returns true if the message box is currently displayed
5185          * @return {Boolean} True if the message box is visible, else false
5186          */
5187         isVisible : function(){
5188             return dlg && dlg.isVisible();  
5189         },
5190
5191         /**
5192          * Hides the message box if it is displayed
5193          */
5194         hide : function(){
5195             if(this.isVisible()){
5196                 dlg.hide();
5197             }  
5198         },
5199
5200         /**
5201          * Displays a new message box, or reinitializes an existing message box, based on the config options
5202          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5203          * The following config object properties are supported:
5204          * <pre>
5205 Property    Type             Description
5206 ----------  ---------------  ------------------------------------------------------------------------------------
5207 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5208                                    closes (defaults to undefined)
5209 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5210                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5211 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5212                                    progress and wait dialogs will ignore this property and always hide the
5213                                    close button as they can only be closed programmatically.
5214 cls               String           A custom CSS class to apply to the message box element
5215 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5216                                    displayed (defaults to 75)
5217 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5218                                    function will be btn (the name of the button that was clicked, if applicable,
5219                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5220                                    Progress and wait dialogs will ignore this option since they do not respond to
5221                                    user actions and can only be closed programmatically, so any required function
5222                                    should be called by the same code after it closes the dialog.
5223 icon              String           A CSS class that provides a background image to be used as an icon for
5224                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5225 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5226 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5227 modal             Boolean          False to allow user interaction with the page while the message box is
5228                                    displayed (defaults to true)
5229 msg               String           A string that will replace the existing message box body text (defaults
5230                                    to the XHTML-compliant non-breaking space character '&#160;')
5231 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5232 progress          Boolean          True to display a progress bar (defaults to false)
5233 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5234 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5235 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5236 title             String           The title text
5237 value             String           The string value to set into the active textbox element if displayed
5238 wait              Boolean          True to display a progress bar (defaults to false)
5239 width             Number           The width of the dialog in pixels
5240 </pre>
5241          *
5242          * Example usage:
5243          * <pre><code>
5244 Roo.Msg.show({
5245    title: 'Address',
5246    msg: 'Please enter your address:',
5247    width: 300,
5248    buttons: Roo.MessageBox.OKCANCEL,
5249    multiline: true,
5250    fn: saveAddress,
5251    animEl: 'addAddressBtn'
5252 });
5253 </code></pre>
5254          * @param {Object} config Configuration options
5255          * @return {Roo.MessageBox} This message box
5256          */
5257         show : function(options)
5258         {
5259             
5260             // this causes nightmares if you show one dialog after another
5261             // especially on callbacks..
5262              
5263             if(this.isVisible()){
5264                 
5265                 this.hide();
5266                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5267                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5268                 Roo.log("New Dialog Message:" +  options.msg )
5269                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5270                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5271                 
5272             }
5273             var d = this.getDialog();
5274             opt = options;
5275             d.setTitle(opt.title || "&#160;");
5276             d.closeEl.setDisplayed(opt.closable !== false);
5277             activeTextEl = textboxEl;
5278             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5279             if(opt.prompt){
5280                 if(opt.multiline){
5281                     textboxEl.hide();
5282                     textareaEl.show();
5283                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5284                         opt.multiline : this.defaultTextHeight);
5285                     activeTextEl = textareaEl;
5286                 }else{
5287                     textboxEl.show();
5288                     textareaEl.hide();
5289                 }
5290             }else{
5291                 textboxEl.hide();
5292                 textareaEl.hide();
5293             }
5294             progressEl.setDisplayed(opt.progress === true);
5295             if (opt.progress) {
5296                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5297             }
5298             this.updateProgress(0);
5299             activeTextEl.dom.value = opt.value || "";
5300             if(opt.prompt){
5301                 dlg.setDefaultButton(activeTextEl);
5302             }else{
5303                 var bs = opt.buttons;
5304                 var db = null;
5305                 if(bs && bs.ok){
5306                     db = buttons["ok"];
5307                 }else if(bs && bs.yes){
5308                     db = buttons["yes"];
5309                 }
5310                 dlg.setDefaultButton(db);
5311             }
5312             bwidth = updateButtons(opt.buttons);
5313             this.updateText(opt.msg);
5314             if(opt.cls){
5315                 d.el.addClass(opt.cls);
5316             }
5317             d.proxyDrag = opt.proxyDrag === true;
5318             d.modal = opt.modal !== false;
5319             d.mask = opt.modal !== false ? mask : false;
5320             if(!d.isVisible()){
5321                 // force it to the end of the z-index stack so it gets a cursor in FF
5322                 document.body.appendChild(dlg.el.dom);
5323                 d.animateTarget = null;
5324                 d.show(options.animEl);
5325             }
5326             return this;
5327         },
5328
5329         /**
5330          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5331          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5332          * and closing the message box when the process is complete.
5333          * @param {String} title The title bar text
5334          * @param {String} msg The message box body text
5335          * @return {Roo.MessageBox} This message box
5336          */
5337         progress : function(title, msg){
5338             this.show({
5339                 title : title,
5340                 msg : msg,
5341                 buttons: false,
5342                 progress:true,
5343                 closable:false,
5344                 minWidth: this.minProgressWidth,
5345                 modal : true
5346             });
5347             return this;
5348         },
5349
5350         /**
5351          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5352          * If a callback function is passed it will be called after the user clicks the button, and the
5353          * id of the button that was clicked will be passed as the only parameter to the callback
5354          * (could also be the top-right close button).
5355          * @param {String} title The title bar text
5356          * @param {String} msg The message box body text
5357          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5358          * @param {Object} scope (optional) The scope of the callback function
5359          * @return {Roo.MessageBox} This message box
5360          */
5361         alert : function(title, msg, fn, scope)
5362         {
5363             this.show({
5364                 title : title,
5365                 msg : msg,
5366                 buttons: this.OK,
5367                 fn: fn,
5368                 closable : false,
5369                 scope : scope,
5370                 modal : true
5371             });
5372             return this;
5373         },
5374
5375         /**
5376          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5377          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5378          * You are responsible for closing the message box when the process is complete.
5379          * @param {String} msg The message box body text
5380          * @param {String} title (optional) The title bar text
5381          * @return {Roo.MessageBox} This message box
5382          */
5383         wait : function(msg, title){
5384             this.show({
5385                 title : title,
5386                 msg : msg,
5387                 buttons: false,
5388                 closable:false,
5389                 progress:true,
5390                 modal:true,
5391                 width:300,
5392                 wait:true
5393             });
5394             waitTimer = Roo.TaskMgr.start({
5395                 run: function(i){
5396                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5397                 },
5398                 interval: 1000
5399             });
5400             return this;
5401         },
5402
5403         /**
5404          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5405          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5406          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5407          * @param {String} title The title bar text
5408          * @param {String} msg The message box body text
5409          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5410          * @param {Object} scope (optional) The scope of the callback function
5411          * @return {Roo.MessageBox} This message box
5412          */
5413         confirm : function(title, msg, fn, scope){
5414             this.show({
5415                 title : title,
5416                 msg : msg,
5417                 buttons: this.YESNO,
5418                 fn: fn,
5419                 scope : scope,
5420                 modal : true
5421             });
5422             return this;
5423         },
5424
5425         /**
5426          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5427          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5428          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5429          * (could also be the top-right close button) and the text that was entered will be passed as the two
5430          * parameters to the callback.
5431          * @param {String} title The title bar text
5432          * @param {String} msg The message box body text
5433          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5434          * @param {Object} scope (optional) The scope of the callback function
5435          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5436          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5437          * @return {Roo.MessageBox} This message box
5438          */
5439         prompt : function(title, msg, fn, scope, multiline){
5440             this.show({
5441                 title : title,
5442                 msg : msg,
5443                 buttons: this.OKCANCEL,
5444                 fn: fn,
5445                 minWidth:250,
5446                 scope : scope,
5447                 prompt:true,
5448                 multiline: multiline,
5449                 modal : true
5450             });
5451             return this;
5452         },
5453
5454         /**
5455          * Button config that displays a single OK button
5456          * @type Object
5457          */
5458         OK : {ok:true},
5459         /**
5460          * Button config that displays Yes and No buttons
5461          * @type Object
5462          */
5463         YESNO : {yes:true, no:true},
5464         /**
5465          * Button config that displays OK and Cancel buttons
5466          * @type Object
5467          */
5468         OKCANCEL : {ok:true, cancel:true},
5469         /**
5470          * Button config that displays Yes, No and Cancel buttons
5471          * @type Object
5472          */
5473         YESNOCANCEL : {yes:true, no:true, cancel:true},
5474
5475         /**
5476          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5477          * @type Number
5478          */
5479         defaultTextHeight : 75,
5480         /**
5481          * The maximum width in pixels of the message box (defaults to 600)
5482          * @type Number
5483          */
5484         maxWidth : 600,
5485         /**
5486          * The minimum width in pixels of the message box (defaults to 100)
5487          * @type Number
5488          */
5489         minWidth : 100,
5490         /**
5491          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5492          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5493          * @type Number
5494          */
5495         minProgressWidth : 250,
5496         /**
5497          * An object containing the default button text strings that can be overriden for localized language support.
5498          * Supported properties are: ok, cancel, yes and no.
5499          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5500          * @type Object
5501          */
5502         buttonText : {
5503             ok : "OK",
5504             cancel : "Cancel",
5505             yes : "Yes",
5506             no : "No"
5507         }
5508     };
5509 }();
5510
5511 /**
5512  * Shorthand for {@link Roo.MessageBox}
5513  */
5514 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5515 Roo.Msg = Roo.Msg || Roo.MessageBox;
5516 /*
5517  * - LGPL
5518  *
5519  * navbar
5520  * 
5521  */
5522
5523 /**
5524  * @class Roo.bootstrap.nav.Bar
5525  * @extends Roo.bootstrap.Component
5526  * @abstract
5527  * Bootstrap Navbar class
5528
5529  * @constructor
5530  * Create a new Navbar
5531  * @param {Object} config The config object
5532  */
5533
5534
5535 Roo.bootstrap.nav.Bar = function(config){
5536     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5537     this.addEvents({
5538         // raw events
5539         /**
5540          * @event beforetoggle
5541          * Fire before toggle the menu
5542          * @param {Roo.EventObject} e
5543          */
5544         "beforetoggle" : true
5545     });
5546 };
5547
5548 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5549     
5550     
5551    
5552     // private
5553     navItems : false,
5554     loadMask : false,
5555     
5556     
5557     getAutoCreate : function(){
5558         
5559         
5560         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5561         
5562     },
5563     
5564     initEvents :function ()
5565     {
5566         //Roo.log(this.el.select('.navbar-toggle',true));
5567         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5568         
5569         var mark = {
5570             tag: "div",
5571             cls:"x-dlg-mask"
5572         };
5573         
5574         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5575         
5576         var size = this.el.getSize();
5577         this.maskEl.setSize(size.width, size.height);
5578         this.maskEl.enableDisplayMode("block");
5579         this.maskEl.hide();
5580         
5581         if(this.loadMask){
5582             this.maskEl.show();
5583         }
5584     },
5585     
5586     
5587     getChildContainer : function()
5588     {
5589         if (this.el && this.el.select('.collapse').getCount()) {
5590             return this.el.select('.collapse',true).first();
5591         }
5592         
5593         return this.el;
5594     },
5595     
5596     mask : function()
5597     {
5598         this.maskEl.show();
5599     },
5600     
5601     unmask : function()
5602     {
5603         this.maskEl.hide();
5604     },
5605     onToggle : function()
5606     {
5607         
5608         if(this.fireEvent('beforetoggle', this) === false){
5609             return;
5610         }
5611         var ce = this.el.select('.navbar-collapse',true).first();
5612       
5613         if (!ce.hasClass('show')) {
5614            this.expand();
5615         } else {
5616             this.collapse();
5617         }
5618         
5619         
5620     
5621     },
5622     /**
5623      * Expand the navbar pulldown 
5624      */
5625     expand : function ()
5626     {
5627        
5628         var ce = this.el.select('.navbar-collapse',true).first();
5629         if (ce.hasClass('collapsing')) {
5630             return;
5631         }
5632         ce.dom.style.height = '';
5633                // show it...
5634         ce.addClass('in'); // old...
5635         ce.removeClass('collapse');
5636         ce.addClass('show');
5637         var h = ce.getHeight();
5638         Roo.log(h);
5639         ce.removeClass('show');
5640         // at this point we should be able to see it..
5641         ce.addClass('collapsing');
5642         
5643         ce.setHeight(0); // resize it ...
5644         ce.on('transitionend', function() {
5645             //Roo.log('done transition');
5646             ce.removeClass('collapsing');
5647             ce.addClass('show');
5648             ce.removeClass('collapse');
5649
5650             ce.dom.style.height = '';
5651         }, this, { single: true} );
5652         ce.setHeight(h);
5653         ce.dom.scrollTop = 0;
5654     },
5655     /**
5656      * Collapse the navbar pulldown 
5657      */
5658     collapse : function()
5659     {
5660          var ce = this.el.select('.navbar-collapse',true).first();
5661        
5662         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5663             // it's collapsed or collapsing..
5664             return;
5665         }
5666         ce.removeClass('in'); // old...
5667         ce.setHeight(ce.getHeight());
5668         ce.removeClass('show');
5669         ce.addClass('collapsing');
5670         
5671         ce.on('transitionend', function() {
5672             ce.dom.style.height = '';
5673             ce.removeClass('collapsing');
5674             ce.addClass('collapse');
5675         }, this, { single: true} );
5676         ce.setHeight(0);
5677     }
5678     
5679     
5680     
5681 });
5682
5683
5684
5685  
5686
5687  /*
5688  * - LGPL
5689  *
5690  * navbar
5691  * 
5692  */
5693
5694 /**
5695  * @class Roo.bootstrap.nav.Simplebar
5696  * @extends Roo.bootstrap.nav.Bar
5697  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5698  * Bootstrap Sidebar class
5699  *
5700  * @cfg {Boolean} inverse is inverted color
5701  * 
5702  * @cfg {String} type (nav | pills | tabs)
5703  * @cfg {Boolean} arrangement stacked | justified
5704  * @cfg {String} align (left | right) alignment
5705  * 
5706  * @cfg {Boolean} main (true|false) main nav bar? default false
5707  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5708  * 
5709  * @cfg {String} tag (header|footer|nav|div) default is nav 
5710
5711  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5712  * 
5713  * 
5714  * @constructor
5715  * Create a new Sidebar
5716  * @param {Object} config The config object
5717  */
5718
5719
5720 Roo.bootstrap.nav.Simplebar = function(config){
5721     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5722 };
5723
5724 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5725     
5726     inverse: false,
5727     
5728     type: false,
5729     arrangement: '',
5730     align : false,
5731     
5732     weight : 'light',
5733     
5734     main : false,
5735     
5736     
5737     tag : false,
5738     
5739     
5740     getAutoCreate : function(){
5741         
5742         
5743         var cfg = {
5744             tag : this.tag || 'div',
5745             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5746         };
5747         if (['light','white'].indexOf(this.weight) > -1) {
5748             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5749         }
5750         cfg.cls += ' bg-' + this.weight;
5751         
5752         if (this.inverse) {
5753             cfg.cls += ' navbar-inverse';
5754             
5755         }
5756         
5757         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5758         
5759         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5760             return cfg;
5761         }
5762         
5763         
5764     
5765         
5766         cfg.cn = [
5767             {
5768                 cls: 'nav nav-' + this.xtype,
5769                 tag : 'ul'
5770             }
5771         ];
5772         
5773          
5774         this.type = this.type || 'nav';
5775         if (['tabs','pills'].indexOf(this.type) != -1) {
5776             cfg.cn[0].cls += ' nav-' + this.type
5777         
5778         
5779         } else {
5780             if (this.type!=='nav') {
5781                 Roo.log('nav type must be nav/tabs/pills')
5782             }
5783             cfg.cn[0].cls += ' navbar-nav'
5784         }
5785         
5786         
5787         
5788         
5789         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5790             cfg.cn[0].cls += ' nav-' + this.arrangement;
5791         }
5792         
5793         
5794         if (this.align === 'right') {
5795             cfg.cn[0].cls += ' navbar-right';
5796         }
5797         
5798         
5799         
5800         
5801         return cfg;
5802     
5803         
5804     }
5805     
5806     
5807     
5808 });
5809
5810
5811
5812  
5813
5814  
5815        /*
5816  * - LGPL
5817  *
5818  * navbar
5819  * navbar-fixed-top
5820  * navbar-expand-md  fixed-top 
5821  */
5822
5823 /**
5824  * @class Roo.bootstrap.nav.Headerbar
5825  * @extends Roo.bootstrap.nav.Simplebar
5826  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5827  * Bootstrap Sidebar class
5828  *
5829  * @cfg {String} brand what is brand
5830  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5831  * @cfg {String} brand_href href of the brand
5832  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5833  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5834  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5835  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5836  * 
5837  * @constructor
5838  * Create a new Sidebar
5839  * @param {Object} config The config object
5840  */
5841
5842
5843 Roo.bootstrap.nav.Headerbar = function(config){
5844     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5845       
5846 };
5847
5848 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5849     
5850     position: '',
5851     brand: '',
5852     brand_href: false,
5853     srButton : true,
5854     autohide : false,
5855     desktopCenter : false,
5856    
5857     
5858     getAutoCreate : function(){
5859         
5860         var   cfg = {
5861             tag: this.nav || 'nav',
5862             cls: 'navbar navbar-expand-md',
5863             role: 'navigation',
5864             cn: []
5865         };
5866         
5867         var cn = cfg.cn;
5868         if (this.desktopCenter) {
5869             cn.push({cls : 'container', cn : []});
5870             cn = cn[0].cn;
5871         }
5872         
5873         if(this.srButton){
5874             var btn = {
5875                 tag: 'button',
5876                 type: 'button',
5877                 cls: 'navbar-toggle navbar-toggler',
5878                 'data-toggle': 'collapse',
5879                 cn: [
5880                     {
5881                         tag: 'span',
5882                         cls: 'sr-only',
5883                         html: 'Toggle navigation'
5884                     },
5885                     {
5886                         tag: 'span',
5887                         cls: 'icon-bar navbar-toggler-icon'
5888                     },
5889                     {
5890                         tag: 'span',
5891                         cls: 'icon-bar'
5892                     },
5893                     {
5894                         tag: 'span',
5895                         cls: 'icon-bar'
5896                     }
5897                 ]
5898             };
5899             
5900             cn.push( Roo.bootstrap.version == 4 ? btn : {
5901                 tag: 'div',
5902                 cls: 'navbar-header',
5903                 cn: [
5904                     btn
5905                 ]
5906             });
5907         }
5908         
5909         cn.push({
5910             tag: 'div',
5911             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5912             cn : []
5913         });
5914         
5915         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5916         
5917         if (['light','white'].indexOf(this.weight) > -1) {
5918             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5919         }
5920         cfg.cls += ' bg-' + this.weight;
5921         
5922         
5923         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5924             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5925             
5926             // tag can override this..
5927             
5928             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5929         }
5930         
5931         if (this.brand !== '') {
5932             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5933             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5934                 tag: 'a',
5935                 href: this.brand_href ? this.brand_href : '#',
5936                 cls: 'navbar-brand',
5937                 cn: [
5938                 this.brand
5939                 ]
5940             });
5941         }
5942         
5943         if(this.main){
5944             cfg.cls += ' main-nav';
5945         }
5946         
5947         
5948         return cfg;
5949
5950         
5951     },
5952     getHeaderChildContainer : function()
5953     {
5954         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5955             return this.el.select('.navbar-header',true).first();
5956         }
5957         
5958         return this.getChildContainer();
5959     },
5960     
5961     getChildContainer : function()
5962     {
5963          
5964         return this.el.select('.roo-navbar-collapse',true).first();
5965          
5966         
5967     },
5968     
5969     initEvents : function()
5970     {
5971         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5972         
5973         if (this.autohide) {
5974             
5975             var prevScroll = 0;
5976             var ft = this.el;
5977             
5978             Roo.get(document).on('scroll',function(e) {
5979                 var ns = Roo.get(document).getScroll().top;
5980                 var os = prevScroll;
5981                 prevScroll = ns;
5982                 
5983                 if(ns > os){
5984                     ft.removeClass('slideDown');
5985                     ft.addClass('slideUp');
5986                     return;
5987                 }
5988                 ft.removeClass('slideUp');
5989                 ft.addClass('slideDown');
5990                  
5991               
5992           },this);
5993         }
5994     }    
5995     
5996 });
5997
5998
5999
6000  
6001
6002  /*
6003  * - LGPL
6004  *
6005  * navbar
6006  * 
6007  */
6008
6009 /**
6010  * @class Roo.bootstrap.nav.Sidebar
6011  * @extends Roo.bootstrap.nav.Bar
6012  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6013  * Bootstrap Sidebar class
6014  * 
6015  * @constructor
6016  * Create a new Sidebar
6017  * @param {Object} config The config object
6018  */
6019
6020
6021 Roo.bootstrap.nav.Sidebar = function(config){
6022     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6023 };
6024
6025 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6026     
6027     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6028     
6029     getAutoCreate : function(){
6030         
6031         
6032         return  {
6033             tag: 'div',
6034             cls: 'sidebar sidebar-nav'
6035         };
6036     
6037         
6038     }
6039     
6040     
6041     
6042 });
6043
6044
6045
6046  
6047
6048  /*
6049  * - LGPL
6050  *
6051  * nav group
6052  * 
6053  */
6054
6055 /**
6056  * @class Roo.bootstrap.nav.Group
6057  * @extends Roo.bootstrap.Component
6058  * @children Roo.bootstrap.nav.Item
6059  * Bootstrap NavGroup class
6060  * @cfg {String} align (left|right)
6061  * @cfg {Boolean} inverse
6062  * @cfg {String} type (nav|pills|tab) default nav
6063  * @cfg {String} navId - reference Id for navbar.
6064  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6065  * 
6066  * @constructor
6067  * Create a new nav group
6068  * @param {Object} config The config object
6069  */
6070
6071 Roo.bootstrap.nav.Group = function(config){
6072     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6073     this.navItems = [];
6074    
6075     Roo.bootstrap.nav.Group.register(this);
6076      this.addEvents({
6077         /**
6078              * @event changed
6079              * Fires when the active item changes
6080              * @param {Roo.bootstrap.nav.Group} this
6081              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6082              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6083          */
6084         'changed': true
6085      });
6086     
6087 };
6088
6089 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6090     
6091     align: '',
6092     inverse: false,
6093     form: false,
6094     type: 'nav',
6095     navId : '',
6096     // private
6097     pilltype : true,
6098     
6099     navItems : false, 
6100     
6101     getAutoCreate : function()
6102     {
6103         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6104         
6105         cfg = {
6106             tag : 'ul',
6107             cls: 'nav' 
6108         };
6109         if (Roo.bootstrap.version == 4) {
6110             if (['tabs','pills'].indexOf(this.type) != -1) {
6111                 cfg.cls += ' nav-' + this.type; 
6112             } else {
6113                 // trying to remove so header bar can right align top?
6114                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6115                     // do not use on header bar... 
6116                     cfg.cls += ' navbar-nav';
6117                 }
6118             }
6119             
6120         } else {
6121             if (['tabs','pills'].indexOf(this.type) != -1) {
6122                 cfg.cls += ' nav-' + this.type
6123             } else {
6124                 if (this.type !== 'nav') {
6125                     Roo.log('nav type must be nav/tabs/pills')
6126                 }
6127                 cfg.cls += ' navbar-nav'
6128             }
6129         }
6130         
6131         if (this.parent() && this.parent().sidebar) {
6132             cfg = {
6133                 tag: 'ul',
6134                 cls: 'dashboard-menu sidebar-menu'
6135             };
6136             
6137             return cfg;
6138         }
6139         
6140         if (this.form === true) {
6141             cfg = {
6142                 tag: 'form',
6143                 cls: 'navbar-form form-inline'
6144             };
6145             //nav navbar-right ml-md-auto
6146             if (this.align === 'right') {
6147                 cfg.cls += ' navbar-right ml-md-auto';
6148             } else {
6149                 cfg.cls += ' navbar-left';
6150             }
6151         }
6152         
6153         if (this.align === 'right') {
6154             cfg.cls += ' navbar-right ml-md-auto';
6155         } else {
6156             cfg.cls += ' mr-auto';
6157         }
6158         
6159         if (this.inverse) {
6160             cfg.cls += ' navbar-inverse';
6161             
6162         }
6163         
6164         
6165         return cfg;
6166     },
6167     /**
6168     * sets the active Navigation item
6169     * @param {Roo.bootstrap.nav.Item} the new current navitem
6170     */
6171     setActiveItem : function(item)
6172     {
6173         var prev = false;
6174         Roo.each(this.navItems, function(v){
6175             if (v == item) {
6176                 return ;
6177             }
6178             if (v.isActive()) {
6179                 v.setActive(false, true);
6180                 prev = v;
6181                 
6182             }
6183             
6184         });
6185
6186         item.setActive(true, true);
6187         this.fireEvent('changed', this, item, prev);
6188         
6189         
6190     },
6191     /**
6192     * gets the active Navigation item
6193     * @return {Roo.bootstrap.nav.Item} the current navitem
6194     */
6195     getActive : function()
6196     {
6197         
6198         var prev = false;
6199         Roo.each(this.navItems, function(v){
6200             
6201             if (v.isActive()) {
6202                 prev = v;
6203                 
6204             }
6205             
6206         });
6207         return prev;
6208     },
6209     
6210     indexOfNav : function()
6211     {
6212         
6213         var prev = false;
6214         Roo.each(this.navItems, function(v,i){
6215             
6216             if (v.isActive()) {
6217                 prev = i;
6218                 
6219             }
6220             
6221         });
6222         return prev;
6223     },
6224     /**
6225     * adds a Navigation item
6226     * @param {Roo.bootstrap.nav.Item} the navitem to add
6227     */
6228     addItem : function(cfg)
6229     {
6230         if (this.form && Roo.bootstrap.version == 4) {
6231             cfg.tag = 'div';
6232         }
6233         var cn = new Roo.bootstrap.nav.Item(cfg);
6234         this.register(cn);
6235         cn.parentId = this.id;
6236         cn.onRender(this.el, null);
6237         return cn;
6238     },
6239     /**
6240     * register a Navigation item
6241     * @param {Roo.bootstrap.nav.Item} the navitem to add
6242     */
6243     register : function(item)
6244     {
6245         this.navItems.push( item);
6246         item.navId = this.navId;
6247     
6248     },
6249     
6250     /**
6251     * clear all the Navigation item
6252     */
6253    
6254     clearAll : function()
6255     {
6256         this.navItems = [];
6257         this.el.dom.innerHTML = '';
6258     },
6259     
6260     getNavItem: function(tabId)
6261     {
6262         var ret = false;
6263         Roo.each(this.navItems, function(e) {
6264             if (e.tabId == tabId) {
6265                ret =  e;
6266                return false;
6267             }
6268             return true;
6269             
6270         });
6271         return ret;
6272     },
6273     
6274     setActiveNext : function()
6275     {
6276         var i = this.indexOfNav(this.getActive());
6277         if (i > this.navItems.length) {
6278             return;
6279         }
6280         this.setActiveItem(this.navItems[i+1]);
6281     },
6282     setActivePrev : function()
6283     {
6284         var i = this.indexOfNav(this.getActive());
6285         if (i  < 1) {
6286             return;
6287         }
6288         this.setActiveItem(this.navItems[i-1]);
6289     },
6290     clearWasActive : function(except) {
6291         Roo.each(this.navItems, function(e) {
6292             if (e.tabId != except.tabId && e.was_active) {
6293                e.was_active = false;
6294                return false;
6295             }
6296             return true;
6297             
6298         });
6299     },
6300     getWasActive : function ()
6301     {
6302         var r = false;
6303         Roo.each(this.navItems, function(e) {
6304             if (e.was_active) {
6305                r = e;
6306                return false;
6307             }
6308             return true;
6309             
6310         });
6311         return r;
6312     }
6313     
6314     
6315 });
6316
6317  
6318 Roo.apply(Roo.bootstrap.nav.Group, {
6319     
6320     groups: {},
6321      /**
6322     * register a Navigation Group
6323     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6324     */
6325     register : function(navgrp)
6326     {
6327         this.groups[navgrp.navId] = navgrp;
6328         
6329     },
6330     /**
6331     * fetch a Navigation Group based on the navigation ID
6332     * @param {string} the navgroup to add
6333     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6334     */
6335     get: function(navId) {
6336         if (typeof(this.groups[navId]) == 'undefined') {
6337             return false;
6338             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6339         }
6340         return this.groups[navId] ;
6341     }
6342     
6343     
6344     
6345 });
6346
6347  /**
6348  * @class Roo.bootstrap.nav.Item
6349  * @extends Roo.bootstrap.Component
6350  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6351  * @parent Roo.bootstrap.nav.Group
6352  * @licence LGPL
6353  * Bootstrap Navbar.NavItem class
6354  * 
6355  * @cfg {String} href  link to
6356  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6357  * @cfg {Boolean} button_outline show and outlined button
6358  * @cfg {String} html content of button
6359  * @cfg {String} badge text inside badge
6360  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6361  * @cfg {String} glyphicon DEPRICATED - use fa
6362  * @cfg {String} icon DEPRICATED - use fa
6363  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6364  * @cfg {Boolean} active Is item active
6365  * @cfg {Boolean} disabled Is item disabled
6366  * @cfg {String} linkcls  Link Class
6367  * @cfg {Boolean} preventDefault (true | false) default false
6368  * @cfg {String} tabId the tab that this item activates.
6369  * @cfg {String} tagtype (a|span) render as a href or span?
6370  * @cfg {Boolean} animateRef (true|false) link to element default false  
6371  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6372   
6373  * @constructor
6374  * Create a new Navbar Item
6375  * @param {Object} config The config object
6376  */
6377 Roo.bootstrap.nav.Item = function(config){
6378     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6379     this.addEvents({
6380         // raw events
6381         /**
6382          * @event click
6383          * The raw click event for the entire grid.
6384          * @param {Roo.EventObject} e
6385          */
6386         "click" : true,
6387          /**
6388             * @event changed
6389             * Fires when the active item active state changes
6390             * @param {Roo.bootstrap.nav.Item} this
6391             * @param {boolean} state the new state
6392              
6393          */
6394         'changed': true,
6395         /**
6396             * @event scrollto
6397             * Fires when scroll to element
6398             * @param {Roo.bootstrap.nav.Item} this
6399             * @param {Object} options
6400             * @param {Roo.EventObject} e
6401              
6402          */
6403         'scrollto': true
6404     });
6405    
6406 };
6407
6408 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6409     
6410     href: false,
6411     html: '',
6412     badge: '',
6413     icon: false,
6414     fa : false,
6415     glyphicon: false,
6416     active: false,
6417     preventDefault : false,
6418     tabId : false,
6419     tagtype : 'a',
6420     tag: 'li',
6421     disabled : false,
6422     animateRef : false,
6423     was_active : false,
6424     button_weight : '',
6425     button_outline : false,
6426     linkcls : '',
6427     navLink: false,
6428     
6429     getAutoCreate : function(){
6430          
6431         var cfg = {
6432             tag: this.tag,
6433             cls: 'nav-item'
6434         };
6435         
6436         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6437         
6438         if (this.active) {
6439             cfg.cls +=  ' active' ;
6440         }
6441         if (this.disabled) {
6442             cfg.cls += ' disabled';
6443         }
6444         
6445         // BS4 only?
6446         if (this.button_weight.length) {
6447             cfg.tag = this.href ? 'a' : 'button';
6448             cfg.html = this.html || '';
6449             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6450             if (this.href) {
6451                 cfg.href = this.href;
6452             }
6453             if (this.fa) {
6454                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6455             } else {
6456                 cfg.cls += " nav-html";
6457             }
6458             
6459             // menu .. should add dropdown-menu class - so no need for carat..
6460             
6461             if (this.badge !== '') {
6462                  
6463                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6464             }
6465             return cfg;
6466         }
6467         
6468         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6469             cfg.cn = [
6470                 {
6471                     tag: this.tagtype,
6472                     href : this.href || "#",
6473                     html: this.html || '',
6474                     cls : ''
6475                 }
6476             ];
6477             if (this.tagtype == 'a') {
6478                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6479         
6480             }
6481             if (this.icon) {
6482                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6483             } else  if (this.fa) {
6484                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6485             } else if(this.glyphicon) {
6486                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6487             } else {
6488                 cfg.cn[0].cls += " nav-html";
6489             }
6490             
6491             if (this.menu) {
6492                 cfg.cn[0].html += " <span class='caret'></span>";
6493              
6494             }
6495             
6496             if (this.badge !== '') {
6497                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6498             }
6499         }
6500         
6501         
6502         
6503         return cfg;
6504     },
6505     onRender : function(ct, position)
6506     {
6507        // Roo.log("Call onRender: " + this.xtype);
6508         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6509             this.tag = 'div';
6510         }
6511         
6512         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6513         this.navLink = this.el.select('.nav-link',true).first();
6514         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6515         return ret;
6516     },
6517       
6518     
6519     initEvents: function() 
6520     {
6521         if (typeof (this.menu) != 'undefined') {
6522             this.menu.parentType = this.xtype;
6523             this.menu.triggerEl = this.el;
6524             this.menu = this.addxtype(Roo.apply({}, this.menu));
6525         }
6526         
6527         this.el.on('click', this.onClick, this);
6528         
6529         //if(this.tagtype == 'span'){
6530         //    this.el.select('span',true).on('click', this.onClick, this);
6531         //}
6532        
6533         // at this point parent should be available..
6534         this.parent().register(this);
6535     },
6536     
6537     onClick : function(e)
6538     {
6539         if (e.getTarget('.dropdown-menu-item')) {
6540             // did you click on a menu itemm.... - then don't trigger onclick..
6541             return;
6542         }
6543         
6544         if(
6545                 this.preventDefault || 
6546                 this.href == '#' 
6547         ){
6548             Roo.log("NavItem - prevent Default?");
6549             e.preventDefault();
6550         }
6551         
6552         if (this.disabled) {
6553             return;
6554         }
6555         
6556         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6557         if (tg && tg.transition) {
6558             Roo.log("waiting for the transitionend");
6559             return;
6560         }
6561         
6562         
6563         
6564         //Roo.log("fire event clicked");
6565         if(this.fireEvent('click', this, e) === false){
6566             return;
6567         };
6568         
6569         if(this.tagtype == 'span'){
6570             return;
6571         }
6572         
6573         //Roo.log(this.href);
6574         var ael = this.el.select('a',true).first();
6575         //Roo.log(ael);
6576         
6577         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6578             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6579             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6580                 return; // ignore... - it's a 'hash' to another page.
6581             }
6582             Roo.log("NavItem - prevent Default?");
6583             e.preventDefault();
6584             this.scrollToElement(e);
6585         }
6586         
6587         
6588         var p =  this.parent();
6589    
6590         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6591             if (typeof(p.setActiveItem) !== 'undefined') {
6592                 p.setActiveItem(this);
6593             }
6594         }
6595         
6596         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6597         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6598             // remove the collapsed menu expand...
6599             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6600         }
6601     },
6602     
6603     isActive: function () {
6604         return this.active
6605     },
6606     setActive : function(state, fire, is_was_active)
6607     {
6608         if (this.active && !state && this.navId) {
6609             this.was_active = true;
6610             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6611             if (nv) {
6612                 nv.clearWasActive(this);
6613             }
6614             
6615         }
6616         this.active = state;
6617         
6618         if (!state ) {
6619             this.el.removeClass('active');
6620             this.navLink ? this.navLink.removeClass('active') : false;
6621         } else if (!this.el.hasClass('active')) {
6622             
6623             this.el.addClass('active');
6624             if (Roo.bootstrap.version == 4 && this.navLink ) {
6625                 this.navLink.addClass('active');
6626             }
6627             
6628         }
6629         if (fire) {
6630             this.fireEvent('changed', this, state);
6631         }
6632         
6633         // show a panel if it's registered and related..
6634         
6635         if (!this.navId || !this.tabId || !state || is_was_active) {
6636             return;
6637         }
6638         
6639         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6640         if (!tg) {
6641             return;
6642         }
6643         var pan = tg.getPanelByName(this.tabId);
6644         if (!pan) {
6645             return;
6646         }
6647         // if we can not flip to new panel - go back to old nav highlight..
6648         if (false == tg.showPanel(pan)) {
6649             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6650             if (nv) {
6651                 var onav = nv.getWasActive();
6652                 if (onav) {
6653                     onav.setActive(true, false, true);
6654                 }
6655             }
6656             
6657         }
6658         
6659         
6660         
6661     },
6662      // this should not be here...
6663     setDisabled : function(state)
6664     {
6665         this.disabled = state;
6666         if (!state ) {
6667             this.el.removeClass('disabled');
6668         } else if (!this.el.hasClass('disabled')) {
6669             this.el.addClass('disabled');
6670         }
6671         
6672     },
6673     
6674     /**
6675      * Fetch the element to display the tooltip on.
6676      * @return {Roo.Element} defaults to this.el
6677      */
6678     tooltipEl : function()
6679     {
6680         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6681     },
6682     
6683     scrollToElement : function(e)
6684     {
6685         var c = document.body;
6686         
6687         /*
6688          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6689          */
6690         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6691             c = document.documentElement;
6692         }
6693         
6694         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6695         
6696         if(!target){
6697             return;
6698         }
6699
6700         var o = target.calcOffsetsTo(c);
6701         
6702         var options = {
6703             target : target,
6704             value : o[1]
6705         };
6706         
6707         this.fireEvent('scrollto', this, options, e);
6708         
6709         Roo.get(c).scrollTo('top', options.value, true);
6710         
6711         return;
6712     },
6713     /**
6714      * Set the HTML (text content) of the item
6715      * @param {string} html  content for the nav item
6716      */
6717     setHtml : function(html)
6718     {
6719         this.html = html;
6720         this.htmlEl.dom.innerHTML = html;
6721         
6722     } 
6723 });
6724  
6725
6726  /*
6727  * - LGPL
6728  *
6729  * sidebar item
6730  *
6731  *  li
6732  *    <span> icon </span>
6733  *    <span> text </span>
6734  *    <span>badge </span>
6735  */
6736
6737 /**
6738  * @class Roo.bootstrap.nav.SidebarItem
6739  * @extends Roo.bootstrap.nav.Item
6740  * Bootstrap Navbar.NavSidebarItem class
6741  * 
6742  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6743  * {Boolean} open is the menu open
6744  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6745  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6746  * {String} buttonSize (sm|md|lg)the extra classes for the button
6747  * {Boolean} showArrow show arrow next to the text (default true)
6748  * @constructor
6749  * Create a new Navbar Button
6750  * @param {Object} config The config object
6751  */
6752 Roo.bootstrap.nav.SidebarItem = function(config){
6753     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6754     this.addEvents({
6755         // raw events
6756         /**
6757          * @event click
6758          * The raw click event for the entire grid.
6759          * @param {Roo.EventObject} e
6760          */
6761         "click" : true,
6762          /**
6763             * @event changed
6764             * Fires when the active item active state changes
6765             * @param {Roo.bootstrap.nav.SidebarItem} this
6766             * @param {boolean} state the new state
6767              
6768          */
6769         'changed': true
6770     });
6771    
6772 };
6773
6774 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6775     
6776     badgeWeight : 'default',
6777     
6778     open: false,
6779     
6780     buttonView : false,
6781     
6782     buttonWeight : 'default',
6783     
6784     buttonSize : 'md',
6785     
6786     showArrow : true,
6787     
6788     getAutoCreate : function(){
6789         
6790         
6791         var a = {
6792                 tag: 'a',
6793                 href : this.href || '#',
6794                 cls: '',
6795                 html : '',
6796                 cn : []
6797         };
6798         
6799         if(this.buttonView){
6800             a = {
6801                 tag: 'button',
6802                 href : this.href || '#',
6803                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6804                 html : this.html,
6805                 cn : []
6806             };
6807         }
6808         
6809         var cfg = {
6810             tag: 'li',
6811             cls: '',
6812             cn: [ a ]
6813         };
6814         
6815         if (this.active) {
6816             cfg.cls += ' active';
6817         }
6818         
6819         if (this.disabled) {
6820             cfg.cls += ' disabled';
6821         }
6822         if (this.open) {
6823             cfg.cls += ' open x-open';
6824         }
6825         // left icon..
6826         if (this.glyphicon || this.icon) {
6827             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6828             a.cn.push({ tag : 'i', cls : c }) ;
6829         }
6830         
6831         if(!this.buttonView){
6832             var span = {
6833                 tag: 'span',
6834                 html : this.html || ''
6835             };
6836
6837             a.cn.push(span);
6838             
6839         }
6840         
6841         if (this.badge !== '') {
6842             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6843         }
6844         
6845         if (this.menu) {
6846             
6847             if(this.showArrow){
6848                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6849             }
6850             
6851             a.cls += ' dropdown-toggle treeview' ;
6852         }
6853         
6854         return cfg;
6855     },
6856     
6857     initEvents : function()
6858     { 
6859         if (typeof (this.menu) != 'undefined') {
6860             this.menu.parentType = this.xtype;
6861             this.menu.triggerEl = this.el;
6862             this.menu = this.addxtype(Roo.apply({}, this.menu));
6863         }
6864         
6865         this.el.on('click', this.onClick, this);
6866         
6867         if(this.badge !== ''){
6868             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6869         }
6870         
6871     },
6872     
6873     onClick : function(e)
6874     {
6875         if(this.disabled){
6876             e.preventDefault();
6877             return;
6878         }
6879         
6880         if(this.preventDefault){
6881             e.preventDefault();
6882         }
6883         
6884         this.fireEvent('click', this, e);
6885     },
6886     
6887     disable : function()
6888     {
6889         this.setDisabled(true);
6890     },
6891     
6892     enable : function()
6893     {
6894         this.setDisabled(false);
6895     },
6896     
6897     setDisabled : function(state)
6898     {
6899         if(this.disabled == state){
6900             return;
6901         }
6902         
6903         this.disabled = state;
6904         
6905         if (state) {
6906             this.el.addClass('disabled');
6907             return;
6908         }
6909         
6910         this.el.removeClass('disabled');
6911         
6912         return;
6913     },
6914     
6915     setActive : function(state)
6916     {
6917         if(this.active == state){
6918             return;
6919         }
6920         
6921         this.active = state;
6922         
6923         if (state) {
6924             this.el.addClass('active');
6925             return;
6926         }
6927         
6928         this.el.removeClass('active');
6929         
6930         return;
6931     },
6932     
6933     isActive: function () 
6934     {
6935         return this.active;
6936     },
6937     
6938     setBadge : function(str)
6939     {
6940         if(!this.badgeEl){
6941             return;
6942         }
6943         
6944         this.badgeEl.dom.innerHTML = str;
6945     }
6946     
6947    
6948      
6949  
6950 });
6951  
6952
6953  /*
6954  * - LGPL
6955  *
6956  * nav progress bar
6957  * 
6958  */
6959
6960 /**
6961  * @class Roo.bootstrap.nav.ProgressBar
6962  * @extends Roo.bootstrap.Component
6963  * @children Roo.bootstrap.nav.ProgressBarItem
6964  * Bootstrap NavProgressBar class
6965  * 
6966  * @constructor
6967  * Create a new nav progress bar - a bar indicating step along a process
6968  * @param {Object} config The config object
6969  */
6970
6971 Roo.bootstrap.nav.ProgressBar = function(config){
6972     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6973
6974     this.bullets = this.bullets || [];
6975    
6976 //    Roo.bootstrap.nav.ProgressBar.register(this);
6977      this.addEvents({
6978         /**
6979              * @event changed
6980              * Fires when the active item changes
6981              * @param {Roo.bootstrap.nav.ProgressBar} this
6982              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6983              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6984          */
6985         'changed': true
6986      });
6987     
6988 };
6989
6990 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
6991     /**
6992      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
6993      * Bullets for the Nav Progress bar for the toolbar
6994      */
6995     bullets : [],
6996     barItems : [],
6997     
6998     getAutoCreate : function()
6999     {
7000         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7001         
7002         cfg = {
7003             tag : 'div',
7004             cls : 'roo-navigation-bar-group',
7005             cn : [
7006                 {
7007                     tag : 'div',
7008                     cls : 'roo-navigation-top-bar'
7009                 },
7010                 {
7011                     tag : 'div',
7012                     cls : 'roo-navigation-bullets-bar',
7013                     cn : [
7014                         {
7015                             tag : 'ul',
7016                             cls : 'roo-navigation-bar'
7017                         }
7018                     ]
7019                 },
7020                 
7021                 {
7022                     tag : 'div',
7023                     cls : 'roo-navigation-bottom-bar'
7024                 }
7025             ]
7026             
7027         };
7028         
7029         return cfg;
7030         
7031     },
7032     
7033     initEvents: function() 
7034     {
7035         
7036     },
7037     
7038     onRender : function(ct, position) 
7039     {
7040         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7041         
7042         if(this.bullets.length){
7043             Roo.each(this.bullets, function(b){
7044                this.addItem(b);
7045             }, this);
7046         }
7047         
7048         this.format();
7049         
7050     },
7051     
7052     addItem : function(cfg)
7053     {
7054         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7055         
7056         item.parentId = this.id;
7057         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7058         
7059         if(cfg.html){
7060             var top = new Roo.bootstrap.Element({
7061                 tag : 'div',
7062                 cls : 'roo-navigation-bar-text'
7063             });
7064             
7065             var bottom = new Roo.bootstrap.Element({
7066                 tag : 'div',
7067                 cls : 'roo-navigation-bar-text'
7068             });
7069             
7070             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7071             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7072             
7073             var topText = new Roo.bootstrap.Element({
7074                 tag : 'span',
7075                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7076             });
7077             
7078             var bottomText = new Roo.bootstrap.Element({
7079                 tag : 'span',
7080                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7081             });
7082             
7083             topText.onRender(top.el, null);
7084             bottomText.onRender(bottom.el, null);
7085             
7086             item.topEl = top;
7087             item.bottomEl = bottom;
7088         }
7089         
7090         this.barItems.push(item);
7091         
7092         return item;
7093     },
7094     
7095     getActive : function()
7096     {
7097         var active = false;
7098         
7099         Roo.each(this.barItems, function(v){
7100             
7101             if (!v.isActive()) {
7102                 return;
7103             }
7104             
7105             active = v;
7106             return false;
7107             
7108         });
7109         
7110         return active;
7111     },
7112     
7113     setActiveItem : function(item)
7114     {
7115         var prev = false;
7116         
7117         Roo.each(this.barItems, function(v){
7118             if (v.rid == item.rid) {
7119                 return ;
7120             }
7121             
7122             if (v.isActive()) {
7123                 v.setActive(false);
7124                 prev = v;
7125             }
7126         });
7127
7128         item.setActive(true);
7129         
7130         this.fireEvent('changed', this, item, prev);
7131     },
7132     
7133     getBarItem: function(rid)
7134     {
7135         var ret = false;
7136         
7137         Roo.each(this.barItems, function(e) {
7138             if (e.rid != rid) {
7139                 return;
7140             }
7141             
7142             ret =  e;
7143             return false;
7144         });
7145         
7146         return ret;
7147     },
7148     
7149     indexOfItem : function(item)
7150     {
7151         var index = false;
7152         
7153         Roo.each(this.barItems, function(v, i){
7154             
7155             if (v.rid != item.rid) {
7156                 return;
7157             }
7158             
7159             index = i;
7160             return false
7161         });
7162         
7163         return index;
7164     },
7165     
7166     setActiveNext : function()
7167     {
7168         var i = this.indexOfItem(this.getActive());
7169         
7170         if (i > this.barItems.length) {
7171             return;
7172         }
7173         
7174         this.setActiveItem(this.barItems[i+1]);
7175     },
7176     
7177     setActivePrev : function()
7178     {
7179         var i = this.indexOfItem(this.getActive());
7180         
7181         if (i  < 1) {
7182             return;
7183         }
7184         
7185         this.setActiveItem(this.barItems[i-1]);
7186     },
7187     
7188     format : function()
7189     {
7190         if(!this.barItems.length){
7191             return;
7192         }
7193      
7194         var width = 100 / this.barItems.length;
7195         
7196         Roo.each(this.barItems, function(i){
7197             i.el.setStyle('width', width + '%');
7198             i.topEl.el.setStyle('width', width + '%');
7199             i.bottomEl.el.setStyle('width', width + '%');
7200         }, this);
7201         
7202     }
7203     
7204 });
7205 /*
7206  * - LGPL
7207  *
7208  * Nav Progress Item
7209  * 
7210  */
7211
7212 /**
7213  * @class Roo.bootstrap.nav.ProgressBarItem
7214  * @extends Roo.bootstrap.Component
7215  * Bootstrap NavProgressBarItem class
7216  * @cfg {String} rid the reference id
7217  * @cfg {Boolean} active (true|false) Is item active default false
7218  * @cfg {Boolean} disabled (true|false) Is item active default false
7219  * @cfg {String} html
7220  * @cfg {String} position (top|bottom) text position default bottom
7221  * @cfg {String} icon show icon instead of number
7222  * 
7223  * @constructor
7224  * Create a new NavProgressBarItem
7225  * @param {Object} config The config object
7226  */
7227 Roo.bootstrap.nav.ProgressBarItem = function(config){
7228     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7229     this.addEvents({
7230         // raw events
7231         /**
7232          * @event click
7233          * The raw click event for the entire grid.
7234          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7235          * @param {Roo.EventObject} e
7236          */
7237         "click" : true
7238     });
7239    
7240 };
7241
7242 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7243     
7244     rid : '',
7245     active : false,
7246     disabled : false,
7247     html : '',
7248     position : 'bottom',
7249     icon : false,
7250     
7251     getAutoCreate : function()
7252     {
7253         var iconCls = 'roo-navigation-bar-item-icon';
7254         
7255         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7256         
7257         var cfg = {
7258             tag: 'li',
7259             cls: 'roo-navigation-bar-item',
7260             cn : [
7261                 {
7262                     tag : 'i',
7263                     cls : iconCls
7264                 }
7265             ]
7266         };
7267         
7268         if(this.active){
7269             cfg.cls += ' active';
7270         }
7271         if(this.disabled){
7272             cfg.cls += ' disabled';
7273         }
7274         
7275         return cfg;
7276     },
7277     
7278     disable : function()
7279     {
7280         this.setDisabled(true);
7281     },
7282     
7283     enable : function()
7284     {
7285         this.setDisabled(false);
7286     },
7287     
7288     initEvents: function() 
7289     {
7290         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7291         
7292         this.iconEl.on('click', this.onClick, this);
7293     },
7294     
7295     onClick : function(e)
7296     {
7297         e.preventDefault();
7298         
7299         if(this.disabled){
7300             return;
7301         }
7302         
7303         if(this.fireEvent('click', this, e) === false){
7304             return;
7305         };
7306         
7307         this.parent().setActiveItem(this);
7308     },
7309     
7310     isActive: function () 
7311     {
7312         return this.active;
7313     },
7314     
7315     setActive : function(state)
7316     {
7317         if(this.active == state){
7318             return;
7319         }
7320         
7321         this.active = state;
7322         
7323         if (state) {
7324             this.el.addClass('active');
7325             return;
7326         }
7327         
7328         this.el.removeClass('active');
7329         
7330         return;
7331     },
7332     
7333     setDisabled : function(state)
7334     {
7335         if(this.disabled == state){
7336             return;
7337         }
7338         
7339         this.disabled = state;
7340         
7341         if (state) {
7342             this.el.addClass('disabled');
7343             return;
7344         }
7345         
7346         this.el.removeClass('disabled');
7347     },
7348     
7349     tooltipEl : function()
7350     {
7351         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7352     }
7353 });
7354  
7355
7356  /*
7357  * - LGPL
7358  *
7359  *  Breadcrumb Nav
7360  * 
7361  */
7362 Roo.namespace('Roo.bootstrap.breadcrumb');
7363
7364
7365 /**
7366  * @class Roo.bootstrap.breadcrumb.Nav
7367  * @extends Roo.bootstrap.Component
7368  * Bootstrap Breadcrumb Nav Class
7369  *  
7370  * @children Roo.bootstrap.breadcrumb.Item
7371  * 
7372  * @constructor
7373  * Create a new breadcrumb.Nav
7374  * @param {Object} config The config object
7375  */
7376
7377
7378 Roo.bootstrap.breadcrumb.Nav = function(config){
7379     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7380     
7381     
7382 };
7383
7384 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7385     
7386     getAutoCreate : function()
7387     {
7388
7389         var cfg = {
7390             tag: 'nav',
7391             cn : [
7392                 {
7393                     tag : 'ol',
7394                     cls : 'breadcrumb'
7395                 }
7396             ]
7397             
7398         };
7399           
7400         return cfg;
7401     },
7402     
7403     initEvents: function()
7404     {
7405         this.olEl = this.el.select('ol',true).first();    
7406     },
7407     getChildContainer : function()
7408     {
7409         return this.olEl;  
7410     }
7411     
7412 });
7413
7414  /*
7415  * - LGPL
7416  *
7417  *  Breadcrumb Item
7418  * 
7419  */
7420
7421
7422 /**
7423  * @class Roo.bootstrap.breadcrumb.Nav
7424  * @extends Roo.bootstrap.Component
7425  * @children Roo.bootstrap.Component
7426  * @parent Roo.bootstrap.breadcrumb.Nav
7427  * Bootstrap Breadcrumb Nav Class
7428  *  
7429  * 
7430  * @cfg {String} html the content of the link.
7431  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7432  * @cfg {Boolean} active is it active
7433
7434  * 
7435  * @constructor
7436  * Create a new breadcrumb.Nav
7437  * @param {Object} config The config object
7438  */
7439
7440 Roo.bootstrap.breadcrumb.Item = function(config){
7441     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7442     this.addEvents({
7443         // img events
7444         /**
7445          * @event click
7446          * The img click event for the img.
7447          * @param {Roo.EventObject} e
7448          */
7449         "click" : true
7450     });
7451     
7452 };
7453
7454 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7455     
7456     href: false,
7457     html : '',
7458     
7459     getAutoCreate : function()
7460     {
7461
7462         var cfg = {
7463             tag: 'li',
7464             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7465         };
7466         if (this.href !== false) {
7467             cfg.cn = [{
7468                 tag : 'a',
7469                 href : this.href,
7470                 html : this.html
7471             }];
7472         } else {
7473             cfg.html = this.html;
7474         }
7475         
7476         return cfg;
7477     },
7478     
7479     initEvents: function()
7480     {
7481         if (this.href) {
7482             this.el.select('a', true).first().on('click',this.onClick, this)
7483         }
7484         
7485     },
7486     onClick : function(e)
7487     {
7488         e.preventDefault();
7489         this.fireEvent('click',this,  e);
7490     }
7491     
7492 });
7493
7494  /*
7495  * - LGPL
7496  *
7497  * row
7498  * 
7499  */
7500
7501 /**
7502  * @class Roo.bootstrap.Row
7503  * @extends Roo.bootstrap.Component
7504  * @children Roo.bootstrap.Component
7505  * Bootstrap Row class (contains columns...)
7506  * 
7507  * @constructor
7508  * Create a new Row
7509  * @param {Object} config The config object
7510  */
7511
7512 Roo.bootstrap.Row = function(config){
7513     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7514 };
7515
7516 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7517     
7518     getAutoCreate : function(){
7519        return {
7520             cls: 'row clearfix'
7521        };
7522     }
7523     
7524     
7525 });
7526
7527  
7528
7529  /*
7530  * - LGPL
7531  *
7532  * pagination
7533  * 
7534  */
7535
7536 /**
7537  * @class Roo.bootstrap.Pagination
7538  * @extends Roo.bootstrap.Component
7539  * @children Roo.bootstrap.Pagination
7540  * Bootstrap Pagination class
7541  * 
7542  * @cfg {String} size (xs|sm|md|lg|xl)
7543  * @cfg {Boolean} inverse 
7544  * 
7545  * @constructor
7546  * Create a new Pagination
7547  * @param {Object} config The config object
7548  */
7549
7550 Roo.bootstrap.Pagination = function(config){
7551     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7552 };
7553
7554 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7555     
7556     cls: false,
7557     size: false,
7558     inverse: false,
7559     
7560     getAutoCreate : function(){
7561         var cfg = {
7562             tag: 'ul',
7563                 cls: 'pagination'
7564         };
7565         if (this.inverse) {
7566             cfg.cls += ' inverse';
7567         }
7568         if (this.html) {
7569             cfg.html=this.html;
7570         }
7571         if (this.cls) {
7572             cfg.cls += " " + this.cls;
7573         }
7574         return cfg;
7575     }
7576    
7577 });
7578
7579  
7580
7581  /*
7582  * - LGPL
7583  *
7584  * Pagination item
7585  * 
7586  */
7587
7588
7589 /**
7590  * @class Roo.bootstrap.PaginationItem
7591  * @extends Roo.bootstrap.Component
7592  * Bootstrap PaginationItem class
7593  * @cfg {String} html text
7594  * @cfg {String} href the link
7595  * @cfg {Boolean} preventDefault (true | false) default true
7596  * @cfg {Boolean} active (true | false) default false
7597  * @cfg {Boolean} disabled default false
7598  * 
7599  * 
7600  * @constructor
7601  * Create a new PaginationItem
7602  * @param {Object} config The config object
7603  */
7604
7605
7606 Roo.bootstrap.PaginationItem = function(config){
7607     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7608     this.addEvents({
7609         // raw events
7610         /**
7611          * @event click
7612          * The raw click event for the entire grid.
7613          * @param {Roo.EventObject} e
7614          */
7615         "click" : true
7616     });
7617 };
7618
7619 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7620     
7621     href : false,
7622     html : false,
7623     preventDefault: true,
7624     active : false,
7625     cls : false,
7626     disabled: false,
7627     
7628     getAutoCreate : function(){
7629         var cfg= {
7630             tag: 'li',
7631             cn: [
7632                 {
7633                     tag : 'a',
7634                     href : this.href ? this.href : '#',
7635                     html : this.html ? this.html : ''
7636                 }
7637             ]
7638         };
7639         
7640         if(this.cls){
7641             cfg.cls = this.cls;
7642         }
7643         
7644         if(this.disabled){
7645             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7646         }
7647         
7648         if(this.active){
7649             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7650         }
7651         
7652         return cfg;
7653     },
7654     
7655     initEvents: function() {
7656         
7657         this.el.on('click', this.onClick, this);
7658         
7659     },
7660     onClick : function(e)
7661     {
7662         Roo.log('PaginationItem on click ');
7663         if(this.preventDefault){
7664             e.preventDefault();
7665         }
7666         
7667         if(this.disabled){
7668             return;
7669         }
7670         
7671         this.fireEvent('click', this, e);
7672     }
7673    
7674 });
7675
7676  
7677
7678  /*
7679  * - LGPL
7680  *
7681  * slider
7682  * 
7683  */
7684
7685
7686 /**
7687  * @class Roo.bootstrap.Slider
7688  * @extends Roo.bootstrap.Component
7689  * Bootstrap Slider class
7690  *    
7691  * @constructor
7692  * Create a new Slider
7693  * @param {Object} config The config object
7694  */
7695
7696 Roo.bootstrap.Slider = function(config){
7697     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7698 };
7699
7700 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7701     
7702     getAutoCreate : function(){
7703         
7704         var cfg = {
7705             tag: 'div',
7706             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7707             cn: [
7708                 {
7709                     tag: 'a',
7710                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7711                 }
7712             ]
7713         };
7714         
7715         return cfg;
7716     }
7717    
7718 });
7719
7720  /*
7721  * Based on:
7722  * Ext JS Library 1.1.1
7723  * Copyright(c) 2006-2007, Ext JS, LLC.
7724  *
7725  * Originally Released Under LGPL - original licence link has changed is not relivant.
7726  *
7727  * Fork - LGPL
7728  * <script type="text/javascript">
7729  */
7730  /**
7731  * @extends Roo.dd.DDProxy
7732  * @class Roo.grid.SplitDragZone
7733  * Support for Column Header resizing
7734  * @constructor
7735  * @param {Object} config
7736  */
7737 // private
7738 // This is a support class used internally by the Grid components
7739 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7740     this.grid = grid;
7741     this.view = grid.getView();
7742     this.proxy = this.view.resizeProxy;
7743     Roo.grid.SplitDragZone.superclass.constructor.call(
7744         this,
7745         hd, // ID
7746         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7747         {  // CONFIG
7748             dragElId : Roo.id(this.proxy.dom),
7749             resizeFrame:false
7750         }
7751     );
7752     
7753     this.setHandleElId(Roo.id(hd));
7754     if (hd2 !== false) {
7755         this.setOuterHandleElId(Roo.id(hd2));
7756     }
7757     
7758     this.scroll = false;
7759 };
7760 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7761     fly: Roo.Element.fly,
7762
7763     b4StartDrag : function(x, y){
7764         this.view.headersDisabled = true;
7765         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7766                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7767         );
7768         this.proxy.setHeight(h);
7769         
7770         // for old system colWidth really stored the actual width?
7771         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7772         // which in reality did not work.. - it worked only for fixed sizes
7773         // for resizable we need to use actual sizes.
7774         var w = this.cm.getColumnWidth(this.cellIndex);
7775         if (!this.view.mainWrap) {
7776             // bootstrap.
7777             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7778         }
7779         
7780         
7781         
7782         // this was w-this.grid.minColumnWidth;
7783         // doesnt really make sense? - w = thie curren width or the rendered one?
7784         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7785         this.resetConstraints();
7786         this.setXConstraint(minw, 1000);
7787         this.setYConstraint(0, 0);
7788         this.minX = x - minw;
7789         this.maxX = x + 1000;
7790         this.startPos = x;
7791         if (!this.view.mainWrap) { // this is Bootstrap code..
7792             this.getDragEl().style.display='block';
7793         }
7794         
7795         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7796     },
7797
7798
7799     handleMouseDown : function(e){
7800         ev = Roo.EventObject.setEvent(e);
7801         var t = this.fly(ev.getTarget());
7802         if(t.hasClass("x-grid-split")){
7803             this.cellIndex = this.view.getCellIndex(t.dom);
7804             this.split = t.dom;
7805             this.cm = this.grid.colModel;
7806             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7807                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7808             }
7809         }
7810     },
7811
7812     endDrag : function(e){
7813         this.view.headersDisabled = false;
7814         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7815         var diff = endX - this.startPos;
7816         // 
7817         var w = this.cm.getColumnWidth(this.cellIndex);
7818         if (!this.view.mainWrap) {
7819             w = 0;
7820         }
7821         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7822     },
7823
7824     autoOffset : function(){
7825         this.setDelta(0,0);
7826     }
7827 });/*
7828  * Based on:
7829  * Ext JS Library 1.1.1
7830  * Copyright(c) 2006-2007, Ext JS, LLC.
7831  *
7832  * Originally Released Under LGPL - original licence link has changed is not relivant.
7833  *
7834  * Fork - LGPL
7835  * <script type="text/javascript">
7836  */
7837
7838 /**
7839  * @class Roo.grid.AbstractSelectionModel
7840  * @extends Roo.util.Observable
7841  * @abstract
7842  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7843  * implemented by descendant classes.  This class should not be directly instantiated.
7844  * @constructor
7845  */
7846 Roo.grid.AbstractSelectionModel = function(){
7847     this.locked = false;
7848     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7849 };
7850
7851 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7852     /** @ignore Called by the grid automatically. Do not call directly. */
7853     init : function(grid){
7854         this.grid = grid;
7855         this.initEvents();
7856     },
7857
7858     /**
7859      * Locks the selections.
7860      */
7861     lock : function(){
7862         this.locked = true;
7863     },
7864
7865     /**
7866      * Unlocks the selections.
7867      */
7868     unlock : function(){
7869         this.locked = false;
7870     },
7871
7872     /**
7873      * Returns true if the selections are locked.
7874      * @return {Boolean}
7875      */
7876     isLocked : function(){
7877         return this.locked;
7878     }
7879 });/*
7880  * Based on:
7881  * Ext JS Library 1.1.1
7882  * Copyright(c) 2006-2007, Ext JS, LLC.
7883  *
7884  * Originally Released Under LGPL - original licence link has changed is not relivant.
7885  *
7886  * Fork - LGPL
7887  * <script type="text/javascript">
7888  */
7889 /**
7890  * @extends Roo.grid.AbstractSelectionModel
7891  * @class Roo.grid.RowSelectionModel
7892  * The default SelectionModel used by {@link Roo.grid.Grid}.
7893  * It supports multiple selections and keyboard selection/navigation. 
7894  * @constructor
7895  * @param {Object} config
7896  */
7897 Roo.grid.RowSelectionModel = function(config){
7898     Roo.apply(this, config);
7899     this.selections = new Roo.util.MixedCollection(false, function(o){
7900         return o.id;
7901     });
7902
7903     this.last = false;
7904     this.lastActive = false;
7905
7906     this.addEvents({
7907         /**
7908         * @event selectionchange
7909         * Fires when the selection changes
7910         * @param {SelectionModel} this
7911         */
7912        "selectionchange" : true,
7913        /**
7914         * @event afterselectionchange
7915         * Fires after the selection changes (eg. by key press or clicking)
7916         * @param {SelectionModel} this
7917         */
7918        "afterselectionchange" : true,
7919        /**
7920         * @event beforerowselect
7921         * Fires when a row is selected being selected, return false to cancel.
7922         * @param {SelectionModel} this
7923         * @param {Number} rowIndex The selected index
7924         * @param {Boolean} keepExisting False if other selections will be cleared
7925         */
7926        "beforerowselect" : true,
7927        /**
7928         * @event rowselect
7929         * Fires when a row is selected.
7930         * @param {SelectionModel} this
7931         * @param {Number} rowIndex The selected index
7932         * @param {Roo.data.Record} r The record
7933         */
7934        "rowselect" : true,
7935        /**
7936         * @event rowdeselect
7937         * Fires when a row is deselected.
7938         * @param {SelectionModel} this
7939         * @param {Number} rowIndex The selected index
7940         */
7941         "rowdeselect" : true
7942     });
7943     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7944     this.locked = false;
7945 };
7946
7947 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7948     /**
7949      * @cfg {Boolean} singleSelect
7950      * True to allow selection of only one row at a time (defaults to false)
7951      */
7952     singleSelect : false,
7953
7954     // private
7955     initEvents : function(){
7956
7957         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7958             this.grid.on("mousedown", this.handleMouseDown, this);
7959         }else{ // allow click to work like normal
7960             this.grid.on("rowclick", this.handleDragableRowClick, this);
7961         }
7962         // bootstrap does not have a view..
7963         var view = this.grid.view ? this.grid.view : this.grid;
7964         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7965             "up" : function(e){
7966                 if(!e.shiftKey){
7967                     this.selectPrevious(e.shiftKey);
7968                 }else if(this.last !== false && this.lastActive !== false){
7969                     var last = this.last;
7970                     this.selectRange(this.last,  this.lastActive-1);
7971                     view.focusRow(this.lastActive);
7972                     if(last !== false){
7973                         this.last = last;
7974                     }
7975                 }else{
7976                     this.selectFirstRow();
7977                 }
7978                 this.fireEvent("afterselectionchange", this);
7979             },
7980             "down" : function(e){
7981                 if(!e.shiftKey){
7982                     this.selectNext(e.shiftKey);
7983                 }else if(this.last !== false && this.lastActive !== false){
7984                     var last = this.last;
7985                     this.selectRange(this.last,  this.lastActive+1);
7986                     view.focusRow(this.lastActive);
7987                     if(last !== false){
7988                         this.last = last;
7989                     }
7990                 }else{
7991                     this.selectFirstRow();
7992                 }
7993                 this.fireEvent("afterselectionchange", this);
7994             },
7995             scope: this
7996         });
7997
7998          
7999         view.on("refresh", this.onRefresh, this);
8000         view.on("rowupdated", this.onRowUpdated, this);
8001         view.on("rowremoved", this.onRemove, this);
8002     },
8003
8004     // private
8005     onRefresh : function(){
8006         var ds = this.grid.ds, i, v = this.grid.view;
8007         var s = this.selections;
8008         s.each(function(r){
8009             if((i = ds.indexOfId(r.id)) != -1){
8010                 v.onRowSelect(i);
8011                 s.add(ds.getAt(i)); // updating the selection relate data
8012             }else{
8013                 s.remove(r);
8014             }
8015         });
8016     },
8017
8018     // private
8019     onRemove : function(v, index, r){
8020         this.selections.remove(r);
8021     },
8022
8023     // private
8024     onRowUpdated : function(v, index, r){
8025         if(this.isSelected(r)){
8026             v.onRowSelect(index);
8027         }
8028     },
8029
8030     /**
8031      * Select records.
8032      * @param {Array} records The records to select
8033      * @param {Boolean} keepExisting (optional) True to keep existing selections
8034      */
8035     selectRecords : function(records, keepExisting){
8036         if(!keepExisting){
8037             this.clearSelections();
8038         }
8039         var ds = this.grid.ds;
8040         for(var i = 0, len = records.length; i < len; i++){
8041             this.selectRow(ds.indexOf(records[i]), true);
8042         }
8043     },
8044
8045     /**
8046      * Gets the number of selected rows.
8047      * @return {Number}
8048      */
8049     getCount : function(){
8050         return this.selections.length;
8051     },
8052
8053     /**
8054      * Selects the first row in the grid.
8055      */
8056     selectFirstRow : function(){
8057         this.selectRow(0);
8058     },
8059
8060     /**
8061      * Select the last row.
8062      * @param {Boolean} keepExisting (optional) True to keep existing selections
8063      */
8064     selectLastRow : function(keepExisting){
8065         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8066     },
8067
8068     /**
8069      * Selects the row immediately following the last selected row.
8070      * @param {Boolean} keepExisting (optional) True to keep existing selections
8071      */
8072     selectNext : function(keepExisting){
8073         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8074             this.selectRow(this.last+1, keepExisting);
8075             var view = this.grid.view ? this.grid.view : this.grid;
8076             view.focusRow(this.last);
8077         }
8078     },
8079
8080     /**
8081      * Selects the row that precedes the last selected row.
8082      * @param {Boolean} keepExisting (optional) True to keep existing selections
8083      */
8084     selectPrevious : function(keepExisting){
8085         if(this.last){
8086             this.selectRow(this.last-1, keepExisting);
8087             var view = this.grid.view ? this.grid.view : this.grid;
8088             view.focusRow(this.last);
8089         }
8090     },
8091
8092     /**
8093      * Returns the selected records
8094      * @return {Array} Array of selected records
8095      */
8096     getSelections : function(){
8097         return [].concat(this.selections.items);
8098     },
8099
8100     /**
8101      * Returns the first selected record.
8102      * @return {Record}
8103      */
8104     getSelected : function(){
8105         return this.selections.itemAt(0);
8106     },
8107
8108
8109     /**
8110      * Clears all selections.
8111      */
8112     clearSelections : function(fast){
8113         if(this.locked) {
8114             return;
8115         }
8116         if(fast !== true){
8117             var ds = this.grid.ds;
8118             var s = this.selections;
8119             s.each(function(r){
8120                 this.deselectRow(ds.indexOfId(r.id));
8121             }, this);
8122             s.clear();
8123         }else{
8124             this.selections.clear();
8125         }
8126         this.last = false;
8127     },
8128
8129
8130     /**
8131      * Selects all rows.
8132      */
8133     selectAll : function(){
8134         if(this.locked) {
8135             return;
8136         }
8137         this.selections.clear();
8138         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8139             this.selectRow(i, true);
8140         }
8141     },
8142
8143     /**
8144      * Returns True if there is a selection.
8145      * @return {Boolean}
8146      */
8147     hasSelection : function(){
8148         return this.selections.length > 0;
8149     },
8150
8151     /**
8152      * Returns True if the specified row is selected.
8153      * @param {Number/Record} record The record or index of the record to check
8154      * @return {Boolean}
8155      */
8156     isSelected : function(index){
8157         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8158         return (r && this.selections.key(r.id) ? true : false);
8159     },
8160
8161     /**
8162      * Returns True if the specified record id is selected.
8163      * @param {String} id The id of record to check
8164      * @return {Boolean}
8165      */
8166     isIdSelected : function(id){
8167         return (this.selections.key(id) ? true : false);
8168     },
8169
8170     // private
8171     handleMouseDown : function(e, t)
8172     {
8173         var view = this.grid.view ? this.grid.view : this.grid;
8174         var rowIndex;
8175         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8176             return;
8177         };
8178         if(e.shiftKey && this.last !== false){
8179             var last = this.last;
8180             this.selectRange(last, rowIndex, e.ctrlKey);
8181             this.last = last; // reset the last
8182             view.focusRow(rowIndex);
8183         }else{
8184             var isSelected = this.isSelected(rowIndex);
8185             if(e.button !== 0 && isSelected){
8186                 view.focusRow(rowIndex);
8187             }else if(e.ctrlKey && isSelected){
8188                 this.deselectRow(rowIndex);
8189             }else if(!isSelected){
8190                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8191                 view.focusRow(rowIndex);
8192             }
8193         }
8194         this.fireEvent("afterselectionchange", this);
8195     },
8196     // private
8197     handleDragableRowClick :  function(grid, rowIndex, e) 
8198     {
8199         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8200             this.selectRow(rowIndex, false);
8201             var view = this.grid.view ? this.grid.view : this.grid;
8202             view.focusRow(rowIndex);
8203              this.fireEvent("afterselectionchange", this);
8204         }
8205     },
8206     
8207     /**
8208      * Selects multiple rows.
8209      * @param {Array} rows Array of the indexes of the row to select
8210      * @param {Boolean} keepExisting (optional) True to keep existing selections
8211      */
8212     selectRows : function(rows, keepExisting){
8213         if(!keepExisting){
8214             this.clearSelections();
8215         }
8216         for(var i = 0, len = rows.length; i < len; i++){
8217             this.selectRow(rows[i], true);
8218         }
8219     },
8220
8221     /**
8222      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8223      * @param {Number} startRow The index of the first row in the range
8224      * @param {Number} endRow The index of the last row in the range
8225      * @param {Boolean} keepExisting (optional) True to retain existing selections
8226      */
8227     selectRange : function(startRow, endRow, keepExisting){
8228         if(this.locked) {
8229             return;
8230         }
8231         if(!keepExisting){
8232             this.clearSelections();
8233         }
8234         if(startRow <= endRow){
8235             for(var i = startRow; i <= endRow; i++){
8236                 this.selectRow(i, true);
8237             }
8238         }else{
8239             for(var i = startRow; i >= endRow; i--){
8240                 this.selectRow(i, true);
8241             }
8242         }
8243     },
8244
8245     /**
8246      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8247      * @param {Number} startRow The index of the first row in the range
8248      * @param {Number} endRow The index of the last row in the range
8249      */
8250     deselectRange : function(startRow, endRow, preventViewNotify){
8251         if(this.locked) {
8252             return;
8253         }
8254         for(var i = startRow; i <= endRow; i++){
8255             this.deselectRow(i, preventViewNotify);
8256         }
8257     },
8258
8259     /**
8260      * Selects a row.
8261      * @param {Number} row The index of the row to select
8262      * @param {Boolean} keepExisting (optional) True to keep existing selections
8263      */
8264     selectRow : function(index, keepExisting, preventViewNotify){
8265         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8266             return;
8267         }
8268         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8269             if(!keepExisting || this.singleSelect){
8270                 this.clearSelections();
8271             }
8272             var r = this.grid.ds.getAt(index);
8273             this.selections.add(r);
8274             this.last = this.lastActive = index;
8275             if(!preventViewNotify){
8276                 var view = this.grid.view ? this.grid.view : this.grid;
8277                 view.onRowSelect(index);
8278             }
8279             this.fireEvent("rowselect", this, index, r);
8280             this.fireEvent("selectionchange", this);
8281         }
8282     },
8283
8284     /**
8285      * Deselects a row.
8286      * @param {Number} row The index of the row to deselect
8287      */
8288     deselectRow : function(index, preventViewNotify){
8289         if(this.locked) {
8290             return;
8291         }
8292         if(this.last == index){
8293             this.last = false;
8294         }
8295         if(this.lastActive == index){
8296             this.lastActive = false;
8297         }
8298         var r = this.grid.ds.getAt(index);
8299         this.selections.remove(r);
8300         if(!preventViewNotify){
8301             var view = this.grid.view ? this.grid.view : this.grid;
8302             view.onRowDeselect(index);
8303         }
8304         this.fireEvent("rowdeselect", this, index);
8305         this.fireEvent("selectionchange", this);
8306     },
8307
8308     // private
8309     restoreLast : function(){
8310         if(this._last){
8311             this.last = this._last;
8312         }
8313     },
8314
8315     // private
8316     acceptsNav : function(row, col, cm){
8317         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8318     },
8319
8320     // private
8321     onEditorKey : function(field, e){
8322         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8323         if(k == e.TAB){
8324             e.stopEvent();
8325             ed.completeEdit();
8326             if(e.shiftKey){
8327                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8328             }else{
8329                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8330             }
8331         }else if(k == e.ENTER && !e.ctrlKey){
8332             e.stopEvent();
8333             ed.completeEdit();
8334             if(e.shiftKey){
8335                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8336             }else{
8337                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8338             }
8339         }else if(k == e.ESC){
8340             ed.cancelEdit();
8341         }
8342         if(newCell){
8343             g.startEditing(newCell[0], newCell[1]);
8344         }
8345     }
8346 });/*
8347  * Based on:
8348  * Ext JS Library 1.1.1
8349  * Copyright(c) 2006-2007, Ext JS, LLC.
8350  *
8351  * Originally Released Under LGPL - original licence link has changed is not relivant.
8352  *
8353  * Fork - LGPL
8354  * <script type="text/javascript">
8355  */
8356  
8357
8358 /**
8359  * @class Roo.grid.ColumnModel
8360  * @extends Roo.util.Observable
8361  * This is the default implementation of a ColumnModel used by the Grid. It defines
8362  * the columns in the grid.
8363  * <br>Usage:<br>
8364  <pre><code>
8365  var colModel = new Roo.grid.ColumnModel([
8366         {header: "Ticker", width: 60, sortable: true, locked: true},
8367         {header: "Company Name", width: 150, sortable: true},
8368         {header: "Market Cap.", width: 100, sortable: true},
8369         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8370         {header: "Employees", width: 100, sortable: true, resizable: false}
8371  ]);
8372  </code></pre>
8373  * <p>
8374  
8375  * The config options listed for this class are options which may appear in each
8376  * individual column definition.
8377  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8378  * @constructor
8379  * @param {Object} config An Array of column config objects. See this class's
8380  * config objects for details.
8381 */
8382 Roo.grid.ColumnModel = function(config){
8383         /**
8384      * The config passed into the constructor
8385      */
8386     this.config = []; //config;
8387     this.lookup = {};
8388
8389     // if no id, create one
8390     // if the column does not have a dataIndex mapping,
8391     // map it to the order it is in the config
8392     for(var i = 0, len = config.length; i < len; i++){
8393         this.addColumn(config[i]);
8394         
8395     }
8396
8397     /**
8398      * The width of columns which have no width specified (defaults to 100)
8399      * @type Number
8400      */
8401     this.defaultWidth = 100;
8402
8403     /**
8404      * Default sortable of columns which have no sortable specified (defaults to false)
8405      * @type Boolean
8406      */
8407     this.defaultSortable = false;
8408
8409     this.addEvents({
8410         /**
8411              * @event widthchange
8412              * Fires when the width of a column changes.
8413              * @param {ColumnModel} this
8414              * @param {Number} columnIndex The column index
8415              * @param {Number} newWidth The new width
8416              */
8417             "widthchange": true,
8418         /**
8419              * @event headerchange
8420              * Fires when the text of a header changes.
8421              * @param {ColumnModel} this
8422              * @param {Number} columnIndex The column index
8423              * @param {Number} newText The new header text
8424              */
8425             "headerchange": true,
8426         /**
8427              * @event hiddenchange
8428              * Fires when a column is hidden or "unhidden".
8429              * @param {ColumnModel} this
8430              * @param {Number} columnIndex The column index
8431              * @param {Boolean} hidden true if hidden, false otherwise
8432              */
8433             "hiddenchange": true,
8434             /**
8435          * @event columnmoved
8436          * Fires when a column is moved.
8437          * @param {ColumnModel} this
8438          * @param {Number} oldIndex
8439          * @param {Number} newIndex
8440          */
8441         "columnmoved" : true,
8442         /**
8443          * @event columlockchange
8444          * Fires when a column's locked state is changed
8445          * @param {ColumnModel} this
8446          * @param {Number} colIndex
8447          * @param {Boolean} locked true if locked
8448          */
8449         "columnlockchange" : true
8450     });
8451     Roo.grid.ColumnModel.superclass.constructor.call(this);
8452 };
8453 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8454     /**
8455      * @cfg {String} header The header text to display in the Grid view.
8456      */
8457         /**
8458      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8459      */
8460         /**
8461      * @cfg {String} smHeader Header at Bootsrap Small width
8462      */
8463         /**
8464      * @cfg {String} mdHeader Header at Bootsrap Medium width
8465      */
8466         /**
8467      * @cfg {String} lgHeader Header at Bootsrap Large width
8468      */
8469         /**
8470      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8471      */
8472     /**
8473      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8474      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8475      * specified, the column's index is used as an index into the Record's data Array.
8476      */
8477     /**
8478      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8479      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8480      */
8481     /**
8482      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8483      * Defaults to the value of the {@link #defaultSortable} property.
8484      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8485      */
8486     /**
8487      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8488      */
8489     /**
8490      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8491      */
8492     /**
8493      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8494      */
8495     /**
8496      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8497      */
8498     /**
8499      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8500      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8501      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8502      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8503      */
8504        /**
8505      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8506      */
8507     /**
8508      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8509      */
8510     /**
8511      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8512      */
8513     /**
8514      * @cfg {String} cursor (Optional)
8515      */
8516     /**
8517      * @cfg {String} tooltip (Optional)
8518      */
8519     /**
8520      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8521      */
8522     /**
8523      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8524      */
8525     /**
8526      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8527      */
8528     /**
8529      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8530      */
8531         /**
8532      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8533      */
8534     /**
8535      * Returns the id of the column at the specified index.
8536      * @param {Number} index The column index
8537      * @return {String} the id
8538      */
8539     getColumnId : function(index){
8540         return this.config[index].id;
8541     },
8542
8543     /**
8544      * Returns the column for a specified id.
8545      * @param {String} id The column id
8546      * @return {Object} the column
8547      */
8548     getColumnById : function(id){
8549         return this.lookup[id];
8550     },
8551
8552     
8553     /**
8554      * Returns the column Object for a specified dataIndex.
8555      * @param {String} dataIndex The column dataIndex
8556      * @return {Object|Boolean} the column or false if not found
8557      */
8558     getColumnByDataIndex: function(dataIndex){
8559         var index = this.findColumnIndex(dataIndex);
8560         return index > -1 ? this.config[index] : false;
8561     },
8562     
8563     /**
8564      * Returns the index for a specified column id.
8565      * @param {String} id The column id
8566      * @return {Number} the index, or -1 if not found
8567      */
8568     getIndexById : function(id){
8569         for(var i = 0, len = this.config.length; i < len; i++){
8570             if(this.config[i].id == id){
8571                 return i;
8572             }
8573         }
8574         return -1;
8575     },
8576     
8577     /**
8578      * Returns the index for a specified column dataIndex.
8579      * @param {String} dataIndex The column dataIndex
8580      * @return {Number} the index, or -1 if not found
8581      */
8582     
8583     findColumnIndex : function(dataIndex){
8584         for(var i = 0, len = this.config.length; i < len; i++){
8585             if(this.config[i].dataIndex == dataIndex){
8586                 return i;
8587             }
8588         }
8589         return -1;
8590     },
8591     
8592     
8593     moveColumn : function(oldIndex, newIndex){
8594         var c = this.config[oldIndex];
8595         this.config.splice(oldIndex, 1);
8596         this.config.splice(newIndex, 0, c);
8597         this.dataMap = null;
8598         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8599     },
8600
8601     isLocked : function(colIndex){
8602         return this.config[colIndex].locked === true;
8603     },
8604
8605     setLocked : function(colIndex, value, suppressEvent){
8606         if(this.isLocked(colIndex) == value){
8607             return;
8608         }
8609         this.config[colIndex].locked = value;
8610         if(!suppressEvent){
8611             this.fireEvent("columnlockchange", this, colIndex, value);
8612         }
8613     },
8614
8615     getTotalLockedWidth : function(){
8616         var totalWidth = 0;
8617         for(var i = 0; i < this.config.length; i++){
8618             if(this.isLocked(i) && !this.isHidden(i)){
8619                 this.totalWidth += this.getColumnWidth(i);
8620             }
8621         }
8622         return totalWidth;
8623     },
8624
8625     getLockedCount : function(){
8626         for(var i = 0, len = this.config.length; i < len; i++){
8627             if(!this.isLocked(i)){
8628                 return i;
8629             }
8630         }
8631         
8632         return this.config.length;
8633     },
8634
8635     /**
8636      * Returns the number of columns.
8637      * @return {Number}
8638      */
8639     getColumnCount : function(visibleOnly){
8640         if(visibleOnly === true){
8641             var c = 0;
8642             for(var i = 0, len = this.config.length; i < len; i++){
8643                 if(!this.isHidden(i)){
8644                     c++;
8645                 }
8646             }
8647             return c;
8648         }
8649         return this.config.length;
8650     },
8651
8652     /**
8653      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8654      * @param {Function} fn
8655      * @param {Object} scope (optional)
8656      * @return {Array} result
8657      */
8658     getColumnsBy : function(fn, scope){
8659         var r = [];
8660         for(var i = 0, len = this.config.length; i < len; i++){
8661             var c = this.config[i];
8662             if(fn.call(scope||this, c, i) === true){
8663                 r[r.length] = c;
8664             }
8665         }
8666         return r;
8667     },
8668
8669     /**
8670      * Returns true if the specified column is sortable.
8671      * @param {Number} col The column index
8672      * @return {Boolean}
8673      */
8674     isSortable : function(col){
8675         if(typeof this.config[col].sortable == "undefined"){
8676             return this.defaultSortable;
8677         }
8678         return this.config[col].sortable;
8679     },
8680
8681     /**
8682      * Returns the rendering (formatting) function defined for the column.
8683      * @param {Number} col The column index.
8684      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8685      */
8686     getRenderer : function(col){
8687         if(!this.config[col].renderer){
8688             return Roo.grid.ColumnModel.defaultRenderer;
8689         }
8690         return this.config[col].renderer;
8691     },
8692
8693     /**
8694      * Sets the rendering (formatting) function for a column.
8695      * @param {Number} col The column index
8696      * @param {Function} fn The function to use to process the cell's raw data
8697      * to return HTML markup for the grid view. The render function is called with
8698      * the following parameters:<ul>
8699      * <li>Data value.</li>
8700      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8701      * <li>css A CSS style string to apply to the table cell.</li>
8702      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8703      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8704      * <li>Row index</li>
8705      * <li>Column index</li>
8706      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8707      */
8708     setRenderer : function(col, fn){
8709         this.config[col].renderer = fn;
8710     },
8711
8712     /**
8713      * Returns the width for the specified column.
8714      * @param {Number} col The column index
8715      * @param (optional) {String} gridSize bootstrap width size.
8716      * @return {Number}
8717      */
8718     getColumnWidth : function(col, gridSize)
8719         {
8720                 var cfg = this.config[col];
8721                 
8722                 if (typeof(gridSize) == 'undefined') {
8723                         return cfg.width * 1 || this.defaultWidth;
8724                 }
8725                 if (gridSize === false) { // if we set it..
8726                         return cfg.width || false;
8727                 }
8728                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8729                 
8730                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8731                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8732                                 continue;
8733                         }
8734                         return cfg[ sizes[i] ];
8735                 }
8736                 return 1;
8737                 
8738     },
8739
8740     /**
8741      * Sets the width for a column.
8742      * @param {Number} col The column index
8743      * @param {Number} width The new width
8744      */
8745     setColumnWidth : function(col, width, suppressEvent){
8746         this.config[col].width = width;
8747         this.totalWidth = null;
8748         if(!suppressEvent){
8749              this.fireEvent("widthchange", this, col, width);
8750         }
8751     },
8752
8753     /**
8754      * Returns the total width of all columns.
8755      * @param {Boolean} includeHidden True to include hidden column widths
8756      * @return {Number}
8757      */
8758     getTotalWidth : function(includeHidden){
8759         if(!this.totalWidth){
8760             this.totalWidth = 0;
8761             for(var i = 0, len = this.config.length; i < len; i++){
8762                 if(includeHidden || !this.isHidden(i)){
8763                     this.totalWidth += this.getColumnWidth(i);
8764                 }
8765             }
8766         }
8767         return this.totalWidth;
8768     },
8769
8770     /**
8771      * Returns the header for the specified column.
8772      * @param {Number} col The column index
8773      * @return {String}
8774      */
8775     getColumnHeader : function(col){
8776         return this.config[col].header;
8777     },
8778
8779     /**
8780      * Sets the header for a column.
8781      * @param {Number} col The column index
8782      * @param {String} header The new header
8783      */
8784     setColumnHeader : function(col, header){
8785         this.config[col].header = header;
8786         this.fireEvent("headerchange", this, col, header);
8787     },
8788
8789     /**
8790      * Returns the tooltip for the specified column.
8791      * @param {Number} col The column index
8792      * @return {String}
8793      */
8794     getColumnTooltip : function(col){
8795             return this.config[col].tooltip;
8796     },
8797     /**
8798      * Sets the tooltip for a column.
8799      * @param {Number} col The column index
8800      * @param {String} tooltip The new tooltip
8801      */
8802     setColumnTooltip : function(col, tooltip){
8803             this.config[col].tooltip = tooltip;
8804     },
8805
8806     /**
8807      * Returns the dataIndex for the specified column.
8808      * @param {Number} col The column index
8809      * @return {Number}
8810      */
8811     getDataIndex : function(col){
8812         return this.config[col].dataIndex;
8813     },
8814
8815     /**
8816      * Sets the dataIndex for a column.
8817      * @param {Number} col The column index
8818      * @param {Number} dataIndex The new dataIndex
8819      */
8820     setDataIndex : function(col, dataIndex){
8821         this.config[col].dataIndex = dataIndex;
8822     },
8823
8824     
8825     
8826     /**
8827      * Returns true if the cell is editable.
8828      * @param {Number} colIndex The column index
8829      * @param {Number} rowIndex The row index - this is nto actually used..?
8830      * @return {Boolean}
8831      */
8832     isCellEditable : function(colIndex, rowIndex){
8833         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8834     },
8835
8836     /**
8837      * Returns the editor defined for the cell/column.
8838      * return false or null to disable editing.
8839      * @param {Number} colIndex The column index
8840      * @param {Number} rowIndex The row index
8841      * @return {Object}
8842      */
8843     getCellEditor : function(colIndex, rowIndex){
8844         return this.config[colIndex].editor;
8845     },
8846
8847     /**
8848      * Sets if a column is editable.
8849      * @param {Number} col The column index
8850      * @param {Boolean} editable True if the column is editable
8851      */
8852     setEditable : function(col, editable){
8853         this.config[col].editable = editable;
8854     },
8855
8856
8857     /**
8858      * Returns true if the column is hidden.
8859      * @param {Number} colIndex The column index
8860      * @return {Boolean}
8861      */
8862     isHidden : function(colIndex){
8863         return this.config[colIndex].hidden;
8864     },
8865
8866
8867     /**
8868      * Returns true if the column width cannot be changed
8869      */
8870     isFixed : function(colIndex){
8871         return this.config[colIndex].fixed;
8872     },
8873
8874     /**
8875      * Returns true if the column can be resized
8876      * @return {Boolean}
8877      */
8878     isResizable : function(colIndex){
8879         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8880     },
8881     /**
8882      * Sets if a column is hidden.
8883      * @param {Number} colIndex The column index
8884      * @param {Boolean} hidden True if the column is hidden
8885      */
8886     setHidden : function(colIndex, hidden){
8887         this.config[colIndex].hidden = hidden;
8888         this.totalWidth = null;
8889         this.fireEvent("hiddenchange", this, colIndex, hidden);
8890     },
8891
8892     /**
8893      * Sets the editor for a column.
8894      * @param {Number} col The column index
8895      * @param {Object} editor The editor object
8896      */
8897     setEditor : function(col, editor){
8898         this.config[col].editor = editor;
8899     },
8900     /**
8901      * Add a column (experimental...) - defaults to adding to the end..
8902      * @param {Object} config 
8903     */
8904     addColumn : function(c)
8905     {
8906     
8907         var i = this.config.length;
8908         this.config[i] = c;
8909         
8910         if(typeof c.dataIndex == "undefined"){
8911             c.dataIndex = i;
8912         }
8913         if(typeof c.renderer == "string"){
8914             c.renderer = Roo.util.Format[c.renderer];
8915         }
8916         if(typeof c.id == "undefined"){
8917             c.id = Roo.id();
8918         }
8919         if(c.editor && c.editor.xtype){
8920             c.editor  = Roo.factory(c.editor, Roo.grid);
8921         }
8922         if(c.editor && c.editor.isFormField){
8923             c.editor = new Roo.grid.GridEditor(c.editor);
8924         }
8925         this.lookup[c.id] = c;
8926     }
8927     
8928 });
8929
8930 Roo.grid.ColumnModel.defaultRenderer = function(value)
8931 {
8932     if(typeof value == "object") {
8933         return value;
8934     }
8935         if(typeof value == "string" && value.length < 1){
8936             return "&#160;";
8937         }
8938     
8939         return String.format("{0}", value);
8940 };
8941
8942 // Alias for backwards compatibility
8943 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8944 /*
8945  * Based on:
8946  * Ext JS Library 1.1.1
8947  * Copyright(c) 2006-2007, Ext JS, LLC.
8948  *
8949  * Originally Released Under LGPL - original licence link has changed is not relivant.
8950  *
8951  * Fork - LGPL
8952  * <script type="text/javascript">
8953  */
8954  
8955 /**
8956  * @class Roo.LoadMask
8957  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8958  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8959  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8960  * element's UpdateManager load indicator and will be destroyed after the initial load.
8961  * @constructor
8962  * Create a new LoadMask
8963  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8964  * @param {Object} config The config object
8965  */
8966 Roo.LoadMask = function(el, config){
8967     this.el = Roo.get(el);
8968     Roo.apply(this, config);
8969     if(this.store){
8970         this.store.on('beforeload', this.onBeforeLoad, this);
8971         this.store.on('load', this.onLoad, this);
8972         this.store.on('loadexception', this.onLoadException, this);
8973         this.removeMask = false;
8974     }else{
8975         var um = this.el.getUpdateManager();
8976         um.showLoadIndicator = false; // disable the default indicator
8977         um.on('beforeupdate', this.onBeforeLoad, this);
8978         um.on('update', this.onLoad, this);
8979         um.on('failure', this.onLoad, this);
8980         this.removeMask = true;
8981     }
8982 };
8983
8984 Roo.LoadMask.prototype = {
8985     /**
8986      * @cfg {Boolean} removeMask
8987      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8988      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8989      */
8990     removeMask : false,
8991     /**
8992      * @cfg {String} msg
8993      * The text to display in a centered loading message box (defaults to 'Loading...')
8994      */
8995     msg : 'Loading...',
8996     /**
8997      * @cfg {String} msgCls
8998      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8999      */
9000     msgCls : 'x-mask-loading',
9001
9002     /**
9003      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9004      * @type Boolean
9005      */
9006     disabled: false,
9007
9008     /**
9009      * Disables the mask to prevent it from being displayed
9010      */
9011     disable : function(){
9012        this.disabled = true;
9013     },
9014
9015     /**
9016      * Enables the mask so that it can be displayed
9017      */
9018     enable : function(){
9019         this.disabled = false;
9020     },
9021     
9022     onLoadException : function()
9023     {
9024         Roo.log(arguments);
9025         
9026         if (typeof(arguments[3]) != 'undefined') {
9027             Roo.MessageBox.alert("Error loading",arguments[3]);
9028         } 
9029         /*
9030         try {
9031             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9032                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9033             }   
9034         } catch(e) {
9035             
9036         }
9037         */
9038     
9039         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9040     },
9041     // private
9042     onLoad : function()
9043     {
9044         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9045     },
9046
9047     // private
9048     onBeforeLoad : function(){
9049         if(!this.disabled){
9050             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9051         }
9052     },
9053
9054     // private
9055     destroy : function(){
9056         if(this.store){
9057             this.store.un('beforeload', this.onBeforeLoad, this);
9058             this.store.un('load', this.onLoad, this);
9059             this.store.un('loadexception', this.onLoadException, this);
9060         }else{
9061             var um = this.el.getUpdateManager();
9062             um.un('beforeupdate', this.onBeforeLoad, this);
9063             um.un('update', this.onLoad, this);
9064             um.un('failure', this.onLoad, this);
9065         }
9066     }
9067 };/**
9068  * @class Roo.bootstrap.Table
9069  * @licence LGBL
9070  * @extends Roo.bootstrap.Component
9071  * @children Roo.bootstrap.TableBody
9072  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9073  * Similar to Roo.grid.Grid
9074  * <pre><code>
9075  var table = Roo.factory({
9076     xtype : 'Table',
9077     xns : Roo.bootstrap,
9078     autoSizeColumns: true,
9079     
9080     
9081     store : {
9082         xtype : 'Store',
9083         xns : Roo.data,
9084         remoteSort : true,
9085         sortInfo : { direction : 'ASC', field: 'name' },
9086         proxy : {
9087            xtype : 'HttpProxy',
9088            xns : Roo.data,
9089            method : 'GET',
9090            url : 'https://example.com/some.data.url.json'
9091         },
9092         reader : {
9093            xtype : 'JsonReader',
9094            xns : Roo.data,
9095            fields : [ 'id', 'name', whatever' ],
9096            id : 'id',
9097            root : 'data'
9098         }
9099     },
9100     cm : [
9101         {
9102             xtype : 'ColumnModel',
9103             xns : Roo.grid,
9104             align : 'center',
9105             cursor : 'pointer',
9106             dataIndex : 'is_in_group',
9107             header : "Name",
9108             sortable : true,
9109             renderer : function(v, x , r) {  
9110             
9111                 return String.format("{0}", v)
9112             }
9113             width : 3
9114         } // more columns..
9115     ],
9116     selModel : {
9117         xtype : 'RowSelectionModel',
9118         xns : Roo.bootstrap.Table
9119         // you can add listeners to catch selection change here....
9120     }
9121      
9122
9123  });
9124  // set any options
9125  grid.render(Roo.get("some-div"));
9126 </code></pre>
9127
9128 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9129
9130
9131
9132  *
9133  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9134  * @cfg {Roo.data.Store} store The data store to use
9135  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9136  * 
9137  * @cfg {String} cls table class
9138  *
9139  * 
9140  * @cfg {boolean} striped Should the rows be alternative striped
9141  * @cfg {boolean} bordered Add borders to the table
9142  * @cfg {boolean} hover Add hover highlighting
9143  * @cfg {boolean} condensed Format condensed
9144  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9145  *                also adds table-responsive (see bootstrap docs for details)
9146  * @cfg {Boolean} loadMask (true|false) default false
9147  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9148  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9149  * @cfg {Boolean} rowSelection (true|false) default false
9150  * @cfg {Boolean} cellSelection (true|false) default false
9151  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9152  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9153  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9154  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9155  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9156  * 
9157  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9158  * 
9159  * @constructor
9160  * Create a new Table
9161  * @param {Object} config The config object
9162  */
9163
9164 Roo.bootstrap.Table = function(config)
9165 {
9166     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9167      
9168     // BC...
9169     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9170     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9171     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9172     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9173     
9174     this.view = this; // compat with grid.
9175     
9176     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9177     if (this.sm) {
9178         this.sm.grid = this;
9179         this.selModel = Roo.factory(this.sm, Roo.grid);
9180         this.sm = this.selModel;
9181         this.sm.xmodule = this.xmodule || false;
9182     }
9183     
9184     if (this.cm && typeof(this.cm.config) == 'undefined') {
9185         this.colModel = new Roo.grid.ColumnModel(this.cm);
9186         this.cm = this.colModel;
9187         this.cm.xmodule = this.xmodule || false;
9188     }
9189     if (this.store) {
9190         this.store= Roo.factory(this.store, Roo.data);
9191         this.ds = this.store;
9192         this.ds.xmodule = this.xmodule || false;
9193          
9194     }
9195     if (this.footer && this.store) {
9196         this.footer.dataSource = this.ds;
9197         this.footer = Roo.factory(this.footer);
9198     }
9199     
9200     /** @private */
9201     this.addEvents({
9202         /**
9203          * @event cellclick
9204          * Fires when a cell is clicked
9205          * @param {Roo.bootstrap.Table} this
9206          * @param {Roo.Element} el
9207          * @param {Number} rowIndex
9208          * @param {Number} columnIndex
9209          * @param {Roo.EventObject} e
9210          */
9211         "cellclick" : true,
9212         /**
9213          * @event celldblclick
9214          * Fires when a cell is double clicked
9215          * @param {Roo.bootstrap.Table} this
9216          * @param {Roo.Element} el
9217          * @param {Number} rowIndex
9218          * @param {Number} columnIndex
9219          * @param {Roo.EventObject} e
9220          */
9221         "celldblclick" : true,
9222         /**
9223          * @event rowclick
9224          * Fires when a row is clicked
9225          * @param {Roo.bootstrap.Table} this
9226          * @param {Roo.Element} el
9227          * @param {Number} rowIndex
9228          * @param {Roo.EventObject} e
9229          */
9230         "rowclick" : true,
9231         /**
9232          * @event rowdblclick
9233          * Fires when a row is double clicked
9234          * @param {Roo.bootstrap.Table} this
9235          * @param {Roo.Element} el
9236          * @param {Number} rowIndex
9237          * @param {Roo.EventObject} e
9238          */
9239         "rowdblclick" : true,
9240         /**
9241          * @event mouseover
9242          * Fires when a mouseover occur
9243          * @param {Roo.bootstrap.Table} this
9244          * @param {Roo.Element} el
9245          * @param {Number} rowIndex
9246          * @param {Number} columnIndex
9247          * @param {Roo.EventObject} e
9248          */
9249         "mouseover" : true,
9250         /**
9251          * @event mouseout
9252          * Fires when a mouseout occur
9253          * @param {Roo.bootstrap.Table} this
9254          * @param {Roo.Element} el
9255          * @param {Number} rowIndex
9256          * @param {Number} columnIndex
9257          * @param {Roo.EventObject} e
9258          */
9259         "mouseout" : true,
9260         /**
9261          * @event rowclass
9262          * Fires when a row is rendered, so you can change add a style to it.
9263          * @param {Roo.bootstrap.Table} this
9264          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9265          */
9266         'rowclass' : true,
9267           /**
9268          * @event rowsrendered
9269          * Fires when all the  rows have been rendered
9270          * @param {Roo.bootstrap.Table} this
9271          */
9272         'rowsrendered' : true,
9273         /**
9274          * @event contextmenu
9275          * The raw contextmenu event for the entire grid.
9276          * @param {Roo.EventObject} e
9277          */
9278         "contextmenu" : true,
9279         /**
9280          * @event rowcontextmenu
9281          * Fires when a row is right clicked
9282          * @param {Roo.bootstrap.Table} this
9283          * @param {Number} rowIndex
9284          * @param {Roo.EventObject} e
9285          */
9286         "rowcontextmenu" : true,
9287         /**
9288          * @event cellcontextmenu
9289          * Fires when a cell is right clicked
9290          * @param {Roo.bootstrap.Table} this
9291          * @param {Number} rowIndex
9292          * @param {Number} cellIndex
9293          * @param {Roo.EventObject} e
9294          */
9295          "cellcontextmenu" : true,
9296          /**
9297          * @event headercontextmenu
9298          * Fires when a header is right clicked
9299          * @param {Roo.bootstrap.Table} this
9300          * @param {Number} columnIndex
9301          * @param {Roo.EventObject} e
9302          */
9303         "headercontextmenu" : true,
9304         /**
9305          * @event mousedown
9306          * The raw mousedown event for the entire grid.
9307          * @param {Roo.EventObject} e
9308          */
9309         "mousedown" : true
9310         
9311     });
9312 };
9313
9314 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9315     
9316     cls: false,
9317     
9318     striped : false,
9319     scrollBody : false,
9320     bordered: false,
9321     hover:  false,
9322     condensed : false,
9323     responsive : false,
9324     sm : false,
9325     cm : false,
9326     store : false,
9327     loadMask : false,
9328     footerShow : true,
9329     headerShow : true,
9330     enableColumnResize: true,
9331   
9332     rowSelection : false,
9333     cellSelection : false,
9334     layout : false,
9335
9336     minColumnWidth : 50,
9337     
9338     // Roo.Element - the tbody
9339     bodyEl: false,  // <tbody> Roo.Element - thead element    
9340     headEl: false,  // <thead> Roo.Element - thead element
9341     resizeProxy : false, // proxy element for dragging?
9342
9343
9344     
9345     container: false, // used by gridpanel...
9346     
9347     lazyLoad : false,
9348     
9349     CSS : Roo.util.CSS,
9350     
9351     auto_hide_footer : false,
9352     
9353     view: false, // actually points to this..
9354     
9355     getAutoCreate : function()
9356     {
9357         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9358         
9359         cfg = {
9360             tag: 'table',
9361             cls : 'table', 
9362             cn : []
9363         };
9364         // this get's auto added by panel.Grid
9365         if (this.scrollBody) {
9366             cfg.cls += ' table-body-fixed';
9367         }    
9368         if (this.striped) {
9369             cfg.cls += ' table-striped';
9370         }
9371         
9372         if (this.hover) {
9373             cfg.cls += ' table-hover';
9374         }
9375         if (this.bordered) {
9376             cfg.cls += ' table-bordered';
9377         }
9378         if (this.condensed) {
9379             cfg.cls += ' table-condensed';
9380         }
9381         
9382         if (this.responsive) {
9383             cfg.cls += ' table-responsive';
9384         }
9385         
9386         if (this.cls) {
9387             cfg.cls+=  ' ' +this.cls;
9388         }
9389         
9390         
9391         
9392         if (this.layout) {
9393             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9394         }
9395         
9396         if(this.store || this.cm){
9397             if(this.headerShow){
9398                 cfg.cn.push(this.renderHeader());
9399             }
9400             
9401             cfg.cn.push(this.renderBody());
9402             
9403             if(this.footerShow){
9404                 cfg.cn.push(this.renderFooter());
9405             }
9406             // where does this come from?
9407             //cfg.cls+=  ' TableGrid';
9408         }
9409         
9410         return { cn : [ cfg ] };
9411     },
9412     
9413     initEvents : function()
9414     {   
9415         if(!this.store || !this.cm){
9416             return;
9417         }
9418         if (this.selModel) {
9419             this.selModel.initEvents();
9420         }
9421         
9422         
9423         //Roo.log('initEvents with ds!!!!');
9424         
9425         this.bodyEl = this.el.select('tbody', true).first();
9426         this.headEl = this.el.select('thead', true).first();
9427         this.mainFoot = this.el.select('tfoot', true).first();
9428         
9429         
9430         
9431         
9432         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9433             e.on('click', this.sort, this);
9434         }, this);
9435         
9436         
9437         // why is this done????? = it breaks dialogs??
9438         //this.parent().el.setStyle('position', 'relative');
9439         
9440         
9441         if (this.footer) {
9442             this.footer.parentId = this.id;
9443             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9444             
9445             if(this.lazyLoad){
9446                 this.el.select('tfoot tr td').first().addClass('hide');
9447             }
9448         } 
9449         
9450         if(this.loadMask) {
9451             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9452         }
9453         
9454         this.store.on('load', this.onLoad, this);
9455         this.store.on('beforeload', this.onBeforeLoad, this);
9456         this.store.on('update', this.onUpdate, this);
9457         this.store.on('add', this.onAdd, this);
9458         this.store.on("clear", this.clear, this);
9459         
9460         this.el.on("contextmenu", this.onContextMenu, this);
9461         
9462         
9463         this.cm.on("headerchange", this.onHeaderChange, this);
9464         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9465
9466  //?? does bodyEl get replaced on render?
9467         this.bodyEl.on("click", this.onClick, this);
9468         this.bodyEl.on("dblclick", this.onDblClick, this);        
9469         this.bodyEl.on('scroll', this.onBodyScroll, this);
9470
9471         // guessing mainbody will work - this relays usually caught by selmodel at present.
9472         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9473   
9474   
9475         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9476         
9477   
9478         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9479             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9480         }
9481         
9482         this.initCSS();
9483     },
9484     // Compatibility with grid - we implement all the view features at present.
9485     getView : function()
9486     {
9487         return this;
9488     },
9489     
9490     initCSS : function()
9491     {
9492         
9493         
9494         var cm = this.cm, styles = [];
9495         this.CSS.removeStyleSheet(this.id + '-cssrules');
9496         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9497         // we can honour xs/sm/md/xl  as widths...
9498         // we first have to decide what widht we are currently at...
9499         var sz = Roo.getGridSize();
9500         
9501         var total = 0;
9502         var last = -1;
9503         var cols = []; // visable cols.
9504         var total_abs = 0;
9505         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9506             var w = cm.getColumnWidth(i, false);
9507             if(cm.isHidden(i)){
9508                 cols.push( { rel : false, abs : 0 });
9509                 continue;
9510             }
9511             if (w !== false) {
9512                 cols.push( { rel : false, abs : w });
9513                 total_abs += w;
9514                 last = i; // not really..
9515                 continue;
9516             }
9517             var w = cm.getColumnWidth(i, sz);
9518             if (w > 0) {
9519                 last = i
9520             }
9521             total += w;
9522             cols.push( { rel : w, abs : false });
9523         }
9524         
9525         var avail = this.bodyEl.dom.clientWidth - total_abs;
9526         
9527         var unitWidth = Math.floor(avail / total);
9528         var rem = avail - (unitWidth * total);
9529         
9530         var hidden, width, pos = 0 , splithide , left;
9531         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9532             
9533             hidden = 'display:none;';
9534             left = '';
9535             width  = 'width:0px;';
9536             splithide = '';
9537             if(!cm.isHidden(i)){
9538                 hidden = '';
9539                 
9540                 
9541                 // we can honour xs/sm/md/xl ?
9542                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9543                 if (w===0) {
9544                     hidden = 'display:none;';
9545                 }
9546                 // width should return a small number...
9547                 if (i == last) {
9548                     w+=rem; // add the remaining with..
9549                 }
9550                 pos += w;
9551                 left = "left:" + (pos -4) + "px;";
9552                 width = "width:" + w+ "px;";
9553                 
9554             }
9555             if (this.responsive) {
9556                 width = '';
9557                 left = '';
9558                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9559                 splithide = 'display: none;';
9560             }
9561             
9562             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9563             if (this.headEl) {
9564                 if (i == last) {
9565                     splithide = 'display:none;';
9566                 }
9567                 
9568                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9569                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9570                             // this is the popover version..
9571                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9572                 );
9573             }
9574             
9575         }
9576         //Roo.log(styles.join(''));
9577         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9578         
9579     },
9580     
9581     
9582     
9583     onContextMenu : function(e, t)
9584     {
9585         this.processEvent("contextmenu", e);
9586     },
9587     
9588     processEvent : function(name, e)
9589     {
9590         if (name != 'touchstart' ) {
9591             this.fireEvent(name, e);    
9592         }
9593         
9594         var t = e.getTarget();
9595         
9596         var cell = Roo.get(t);
9597         
9598         if(!cell){
9599             return;
9600         }
9601         
9602         if(cell.findParent('tfoot', false, true)){
9603             return;
9604         }
9605         
9606         if(cell.findParent('thead', false, true)){
9607             
9608             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9609                 cell = Roo.get(t).findParent('th', false, true);
9610                 if (!cell) {
9611                     Roo.log("failed to find th in thead?");
9612                     Roo.log(e.getTarget());
9613                     return;
9614                 }
9615             }
9616             
9617             var cellIndex = cell.dom.cellIndex;
9618             
9619             var ename = name == 'touchstart' ? 'click' : name;
9620             this.fireEvent("header" + ename, this, cellIndex, e);
9621             
9622             return;
9623         }
9624         
9625         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9626             cell = Roo.get(t).findParent('td', false, true);
9627             if (!cell) {
9628                 Roo.log("failed to find th in tbody?");
9629                 Roo.log(e.getTarget());
9630                 return;
9631             }
9632         }
9633         
9634         var row = cell.findParent('tr', false, true);
9635         var cellIndex = cell.dom.cellIndex;
9636         var rowIndex = row.dom.rowIndex - 1;
9637         
9638         if(row !== false){
9639             
9640             this.fireEvent("row" + name, this, rowIndex, e);
9641             
9642             if(cell !== false){
9643             
9644                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9645             }
9646         }
9647         
9648     },
9649     
9650     onMouseover : function(e, el)
9651     {
9652         var cell = Roo.get(el);
9653         
9654         if(!cell){
9655             return;
9656         }
9657         
9658         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9659             cell = cell.findParent('td', false, true);
9660         }
9661         
9662         var row = cell.findParent('tr', false, true);
9663         var cellIndex = cell.dom.cellIndex;
9664         var rowIndex = row.dom.rowIndex - 1; // start from 0
9665         
9666         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9667         
9668     },
9669     
9670     onMouseout : function(e, el)
9671     {
9672         var cell = Roo.get(el);
9673         
9674         if(!cell){
9675             return;
9676         }
9677         
9678         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9679             cell = cell.findParent('td', false, true);
9680         }
9681         
9682         var row = cell.findParent('tr', false, true);
9683         var cellIndex = cell.dom.cellIndex;
9684         var rowIndex = row.dom.rowIndex - 1; // start from 0
9685         
9686         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9687         
9688     },
9689     
9690     onClick : function(e, el)
9691     {
9692         var cell = Roo.get(el);
9693         
9694         if(!cell || (!this.cellSelection && !this.rowSelection)){
9695             return;
9696         }
9697         
9698         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9699             cell = cell.findParent('td', false, true);
9700         }
9701         
9702         if(!cell || typeof(cell) == 'undefined'){
9703             return;
9704         }
9705         
9706         var row = cell.findParent('tr', false, true);
9707         
9708         if(!row || typeof(row) == 'undefined'){
9709             return;
9710         }
9711         
9712         var cellIndex = cell.dom.cellIndex;
9713         var rowIndex = this.getRowIndex(row);
9714         
9715         // why??? - should these not be based on SelectionModel?
9716         //if(this.cellSelection){
9717             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9718         //}
9719         
9720         //if(this.rowSelection){
9721             this.fireEvent('rowclick', this, row, rowIndex, e);
9722         //}
9723          
9724     },
9725         
9726     onDblClick : function(e,el)
9727     {
9728         var cell = Roo.get(el);
9729         
9730         if(!cell || (!this.cellSelection && !this.rowSelection)){
9731             return;
9732         }
9733         
9734         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9735             cell = cell.findParent('td', false, true);
9736         }
9737         
9738         if(!cell || typeof(cell) == 'undefined'){
9739             return;
9740         }
9741         
9742         var row = cell.findParent('tr', false, true);
9743         
9744         if(!row || typeof(row) == 'undefined'){
9745             return;
9746         }
9747         
9748         var cellIndex = cell.dom.cellIndex;
9749         var rowIndex = this.getRowIndex(row);
9750         
9751         if(this.cellSelection){
9752             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9753         }
9754         
9755         if(this.rowSelection){
9756             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9757         }
9758     },
9759     findRowIndex : function(el)
9760     {
9761         var cell = Roo.get(el);
9762         if(!cell) {
9763             return false;
9764         }
9765         var row = cell.findParent('tr', false, true);
9766         
9767         if(!row || typeof(row) == 'undefined'){
9768             return false;
9769         }
9770         return this.getRowIndex(row);
9771     },
9772     sort : function(e,el)
9773     {
9774         var col = Roo.get(el);
9775         
9776         if(!col.hasClass('sortable')){
9777             return;
9778         }
9779         
9780         var sort = col.attr('sort');
9781         var dir = 'ASC';
9782         
9783         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9784             dir = 'DESC';
9785         }
9786         
9787         this.store.sortInfo = {field : sort, direction : dir};
9788         
9789         if (this.footer) {
9790             Roo.log("calling footer first");
9791             this.footer.onClick('first');
9792         } else {
9793         
9794             this.store.load({ params : { start : 0 } });
9795         }
9796     },
9797     
9798     renderHeader : function()
9799     {
9800         var header = {
9801             tag: 'thead',
9802             cn : []
9803         };
9804         
9805         var cm = this.cm;
9806         this.totalWidth = 0;
9807         
9808         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9809             
9810             var config = cm.config[i];
9811             
9812             var c = {
9813                 tag: 'th',
9814                 cls : 'x-hcol-' + i,
9815                 style : '',
9816                 
9817                 html: cm.getColumnHeader(i)
9818             };
9819             
9820             var tooltip = cm.getColumnTooltip(i);
9821             if (tooltip) {
9822                 c.tooltip = tooltip;
9823             }
9824             
9825             
9826             var hh = '';
9827             
9828             if(typeof(config.sortable) != 'undefined' && config.sortable){
9829                 c.cls += ' sortable';
9830                 c.html = '<i class="fa"></i>' + c.html;
9831             }
9832             
9833             // could use BS4 hidden-..-down 
9834             
9835             if(typeof(config.lgHeader) != 'undefined'){
9836                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9837             }
9838             
9839             if(typeof(config.mdHeader) != 'undefined'){
9840                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9841             }
9842             
9843             if(typeof(config.smHeader) != 'undefined'){
9844                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9845             }
9846             
9847             if(typeof(config.xsHeader) != 'undefined'){
9848                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9849             }
9850             
9851             if(hh.length){
9852                 c.html = hh;
9853             }
9854             
9855             if(typeof(config.tooltip) != 'undefined'){
9856                 c.tooltip = config.tooltip;
9857             }
9858             
9859             if(typeof(config.colspan) != 'undefined'){
9860                 c.colspan = config.colspan;
9861             }
9862             
9863             // hidden is handled by CSS now
9864             
9865             if(typeof(config.dataIndex) != 'undefined'){
9866                 c.sort = config.dataIndex;
9867             }
9868             
9869            
9870             
9871             if(typeof(config.align) != 'undefined' && config.align.length){
9872                 c.style += ' text-align:' + config.align + ';';
9873             }
9874             
9875             /* width is done in CSS
9876              *if(typeof(config.width) != 'undefined'){
9877                 c.style += ' width:' + config.width + 'px;';
9878                 this.totalWidth += config.width;
9879             } else {
9880                 this.totalWidth += 100; // assume minimum of 100 per column?
9881             }
9882             */
9883             
9884             if(typeof(config.cls) != 'undefined'){
9885                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9886             }
9887             // this is the bit that doesnt reall work at all...
9888             
9889             if (this.responsive) {
9890                  
9891             
9892                 ['xs','sm','md','lg'].map(function(size){
9893                     
9894                     if(typeof(config[size]) == 'undefined'){
9895                         return;
9896                     }
9897                      
9898                     if (!config[size]) { // 0 = hidden
9899                         // BS 4 '0' is treated as hide that column and below.
9900                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9901                         return;
9902                     }
9903                     
9904                     c.cls += ' col-' + size + '-' + config[size] + (
9905                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9906                     );
9907                     
9908                     
9909                 });
9910             }
9911             // at the end?
9912             
9913             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9914             
9915             
9916             
9917             
9918             header.cn.push(c)
9919         }
9920         
9921         return header;
9922     },
9923     
9924     renderBody : function()
9925     {
9926         var body = {
9927             tag: 'tbody',
9928             cn : [
9929                 {
9930                     tag: 'tr',
9931                     cn : [
9932                         {
9933                             tag : 'td',
9934                             colspan :  this.cm.getColumnCount()
9935                         }
9936                     ]
9937                 }
9938             ]
9939         };
9940         
9941         return body;
9942     },
9943     
9944     renderFooter : function()
9945     {
9946         var footer = {
9947             tag: 'tfoot',
9948             cn : [
9949                 {
9950                     tag: 'tr',
9951                     cn : [
9952                         {
9953                             tag : 'td',
9954                             colspan :  this.cm.getColumnCount()
9955                         }
9956                     ]
9957                 }
9958             ]
9959         };
9960         
9961         return footer;
9962     },
9963     
9964     
9965     
9966     onLoad : function()
9967     {
9968 //        Roo.log('ds onload');
9969         this.clear();
9970         
9971         var _this = this;
9972         var cm = this.cm;
9973         var ds = this.store;
9974         
9975         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9976             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9977             if (_this.store.sortInfo) {
9978                     
9979                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9980                     e.select('i', true).addClass(['fa-arrow-up']);
9981                 }
9982                 
9983                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9984                     e.select('i', true).addClass(['fa-arrow-down']);
9985                 }
9986             }
9987         });
9988         
9989         var tbody =  this.bodyEl;
9990               
9991         if(ds.getCount() > 0){
9992             ds.data.each(function(d,rowIndex){
9993                 var row =  this.renderRow(cm, ds, rowIndex);
9994                 
9995                 tbody.createChild(row);
9996                 
9997                 var _this = this;
9998                 
9999                 if(row.cellObjects.length){
10000                     Roo.each(row.cellObjects, function(r){
10001                         _this.renderCellObject(r);
10002                     })
10003                 }
10004                 
10005             }, this);
10006         }
10007         
10008         var tfoot = this.el.select('tfoot', true).first();
10009         
10010         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10011             
10012             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10013             
10014             var total = this.ds.getTotalCount();
10015             
10016             if(this.footer.pageSize < total){
10017                 this.mainFoot.show();
10018             }
10019         }
10020         
10021         Roo.each(this.el.select('tbody td', true).elements, function(e){
10022             e.on('mouseover', _this.onMouseover, _this);
10023         });
10024         
10025         Roo.each(this.el.select('tbody td', true).elements, function(e){
10026             e.on('mouseout', _this.onMouseout, _this);
10027         });
10028         this.fireEvent('rowsrendered', this);
10029         
10030         this.autoSize();
10031         
10032         this.initCSS(); /// resize cols
10033
10034         
10035     },
10036     
10037     
10038     onUpdate : function(ds,record)
10039     {
10040         this.refreshRow(record);
10041         this.autoSize();
10042     },
10043     
10044     onRemove : function(ds, record, index, isUpdate){
10045         if(isUpdate !== true){
10046             this.fireEvent("beforerowremoved", this, index, record);
10047         }
10048         var bt = this.bodyEl.dom;
10049         
10050         var rows = this.el.select('tbody > tr', true).elements;
10051         
10052         if(typeof(rows[index]) != 'undefined'){
10053             bt.removeChild(rows[index].dom);
10054         }
10055         
10056 //        if(bt.rows[index]){
10057 //            bt.removeChild(bt.rows[index]);
10058 //        }
10059         
10060         if(isUpdate !== true){
10061             //this.stripeRows(index);
10062             //this.syncRowHeights(index, index);
10063             //this.layout();
10064             this.fireEvent("rowremoved", this, index, record);
10065         }
10066     },
10067     
10068     onAdd : function(ds, records, rowIndex)
10069     {
10070         //Roo.log('on Add called');
10071         // - note this does not handle multiple adding very well..
10072         var bt = this.bodyEl.dom;
10073         for (var i =0 ; i < records.length;i++) {
10074             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10075             //Roo.log(records[i]);
10076             //Roo.log(this.store.getAt(rowIndex+i));
10077             this.insertRow(this.store, rowIndex + i, false);
10078             return;
10079         }
10080         
10081     },
10082     
10083     
10084     refreshRow : function(record){
10085         var ds = this.store, index;
10086         if(typeof record == 'number'){
10087             index = record;
10088             record = ds.getAt(index);
10089         }else{
10090             index = ds.indexOf(record);
10091             if (index < 0) {
10092                 return; // should not happen - but seems to 
10093             }
10094         }
10095         this.insertRow(ds, index, true);
10096         this.autoSize();
10097         this.onRemove(ds, record, index+1, true);
10098         this.autoSize();
10099         //this.syncRowHeights(index, index);
10100         //this.layout();
10101         this.fireEvent("rowupdated", this, index, record);
10102     },
10103     // private - called by RowSelection
10104     onRowSelect : function(rowIndex){
10105         var row = this.getRowDom(rowIndex);
10106         row.addClass(['bg-info','info']);
10107     },
10108     // private - called by RowSelection
10109     onRowDeselect : function(rowIndex)
10110     {
10111         if (rowIndex < 0) {
10112             return;
10113         }
10114         var row = this.getRowDom(rowIndex);
10115         row.removeClass(['bg-info','info']);
10116     },
10117       /**
10118      * Focuses the specified row.
10119      * @param {Number} row The row index
10120      */
10121     focusRow : function(row)
10122     {
10123         //Roo.log('GridView.focusRow');
10124         var x = this.bodyEl.dom.scrollLeft;
10125         this.focusCell(row, 0, false);
10126         this.bodyEl.dom.scrollLeft = x;
10127
10128     },
10129      /**
10130      * Focuses the specified cell.
10131      * @param {Number} row The row index
10132      * @param {Number} col The column index
10133      * @param {Boolean} hscroll false to disable horizontal scrolling
10134      */
10135     focusCell : function(row, col, hscroll)
10136     {
10137         //Roo.log('GridView.focusCell');
10138         var el = this.ensureVisible(row, col, hscroll);
10139         // not sure what focusEL achives = it's a <a> pos relative 
10140         //this.focusEl.alignTo(el, "tl-tl");
10141         //if(Roo.isGecko){
10142         //    this.focusEl.focus();
10143         //}else{
10144         //    this.focusEl.focus.defer(1, this.focusEl);
10145         //}
10146     },
10147     
10148      /**
10149      * Scrolls the specified cell into view
10150      * @param {Number} row The row index
10151      * @param {Number} col The column index
10152      * @param {Boolean} hscroll false to disable horizontal scrolling
10153      */
10154     ensureVisible : function(row, col, hscroll)
10155     {
10156         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10157         //return null; //disable for testing.
10158         if(typeof row != "number"){
10159             row = row.rowIndex;
10160         }
10161         if(row < 0 && row >= this.ds.getCount()){
10162             return  null;
10163         }
10164         col = (col !== undefined ? col : 0);
10165         var cm = this.cm;
10166         while(cm.isHidden(col)){
10167             col++;
10168         }
10169
10170         var el = this.getCellDom(row, col);
10171         if(!el){
10172             return null;
10173         }
10174         var c = this.bodyEl.dom;
10175
10176         var ctop = parseInt(el.offsetTop, 10);
10177         var cleft = parseInt(el.offsetLeft, 10);
10178         var cbot = ctop + el.offsetHeight;
10179         var cright = cleft + el.offsetWidth;
10180
10181         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10182         var ch = 0; //?? header is not withing the area?
10183         var stop = parseInt(c.scrollTop, 10);
10184         var sleft = parseInt(c.scrollLeft, 10);
10185         var sbot = stop + ch;
10186         var sright = sleft + c.clientWidth;
10187         /*
10188         Roo.log('GridView.ensureVisible:' +
10189                 ' ctop:' + ctop +
10190                 ' c.clientHeight:' + c.clientHeight +
10191                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10192                 ' stop:' + stop +
10193                 ' cbot:' + cbot +
10194                 ' sbot:' + sbot +
10195                 ' ch:' + ch  
10196                 );
10197         */
10198         if(ctop < stop){
10199             c.scrollTop = ctop;
10200             //Roo.log("set scrolltop to ctop DISABLE?");
10201         }else if(cbot > sbot){
10202             //Roo.log("set scrolltop to cbot-ch");
10203             c.scrollTop = cbot-ch;
10204         }
10205
10206         if(hscroll !== false){
10207             if(cleft < sleft){
10208                 c.scrollLeft = cleft;
10209             }else if(cright > sright){
10210                 c.scrollLeft = cright-c.clientWidth;
10211             }
10212         }
10213
10214         return el;
10215     },
10216     
10217     
10218     insertRow : function(dm, rowIndex, isUpdate){
10219         
10220         if(!isUpdate){
10221             this.fireEvent("beforerowsinserted", this, rowIndex);
10222         }
10223             //var s = this.getScrollState();
10224         var row = this.renderRow(this.cm, this.store, rowIndex);
10225         // insert before rowIndex..
10226         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10227         
10228         var _this = this;
10229                 
10230         if(row.cellObjects.length){
10231             Roo.each(row.cellObjects, function(r){
10232                 _this.renderCellObject(r);
10233             })
10234         }
10235             
10236         if(!isUpdate){
10237             this.fireEvent("rowsinserted", this, rowIndex);
10238             //this.syncRowHeights(firstRow, lastRow);
10239             //this.stripeRows(firstRow);
10240             //this.layout();
10241         }
10242         
10243     },
10244     
10245     
10246     getRowDom : function(rowIndex)
10247     {
10248         var rows = this.el.select('tbody > tr', true).elements;
10249         
10250         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10251         
10252     },
10253     getCellDom : function(rowIndex, colIndex)
10254     {
10255         var row = this.getRowDom(rowIndex);
10256         if (row === false) {
10257             return false;
10258         }
10259         var cols = row.select('td', true).elements;
10260         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10261         
10262     },
10263     
10264     // returns the object tree for a tr..
10265   
10266     
10267     renderRow : function(cm, ds, rowIndex) 
10268     {
10269         var d = ds.getAt(rowIndex);
10270         
10271         var row = {
10272             tag : 'tr',
10273             cls : 'x-row-' + rowIndex,
10274             cn : []
10275         };
10276             
10277         var cellObjects = [];
10278         
10279         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10280             var config = cm.config[i];
10281             
10282             var renderer = cm.getRenderer(i);
10283             var value = '';
10284             var id = false;
10285             
10286             if(typeof(renderer) !== 'undefined'){
10287                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10288             }
10289             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10290             // and are rendered into the cells after the row is rendered - using the id for the element.
10291             
10292             if(typeof(value) === 'object'){
10293                 id = Roo.id();
10294                 cellObjects.push({
10295                     container : id,
10296                     cfg : value 
10297                 })
10298             }
10299             
10300             var rowcfg = {
10301                 record: d,
10302                 rowIndex : rowIndex,
10303                 colIndex : i,
10304                 rowClass : ''
10305             };
10306
10307             this.fireEvent('rowclass', this, rowcfg);
10308             
10309             var td = {
10310                 tag: 'td',
10311                 // this might end up displaying HTML?
10312                 // this is too messy... - better to only do it on columsn you know are going to be too long
10313                 //tooltip : (typeof(value) === 'object') ? '' : value,
10314                 cls : rowcfg.rowClass + ' x-col-' + i,
10315                 style: '',
10316                 html: (typeof(value) === 'object') ? '' : value
10317             };
10318             
10319             if (id) {
10320                 td.id = id;
10321             }
10322             
10323             if(typeof(config.colspan) != 'undefined'){
10324                 td.colspan = config.colspan;
10325             }
10326             
10327             
10328             
10329             if(typeof(config.align) != 'undefined' && config.align.length){
10330                 td.style += ' text-align:' + config.align + ';';
10331             }
10332             if(typeof(config.valign) != 'undefined' && config.valign.length){
10333                 td.style += ' vertical-align:' + config.valign + ';';
10334             }
10335             /*
10336             if(typeof(config.width) != 'undefined'){
10337                 td.style += ' width:' +  config.width + 'px;';
10338             }
10339             */
10340             
10341             if(typeof(config.cursor) != 'undefined'){
10342                 td.style += ' cursor:' +  config.cursor + ';';
10343             }
10344             
10345             if(typeof(config.cls) != 'undefined'){
10346                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10347             }
10348             if (this.responsive) {
10349                 ['xs','sm','md','lg'].map(function(size){
10350                     
10351                     if(typeof(config[size]) == 'undefined'){
10352                         return;
10353                     }
10354                     
10355                     
10356                       
10357                     if (!config[size]) { // 0 = hidden
10358                         // BS 4 '0' is treated as hide that column and below.
10359                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10360                         return;
10361                     }
10362                     
10363                     td.cls += ' col-' + size + '-' + config[size] + (
10364                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10365                     );
10366                      
10367     
10368                 });
10369             }
10370             row.cn.push(td);
10371            
10372         }
10373         
10374         row.cellObjects = cellObjects;
10375         
10376         return row;
10377           
10378     },
10379     
10380     
10381     
10382     onBeforeLoad : function()
10383     {
10384         
10385     },
10386      /**
10387      * Remove all rows
10388      */
10389     clear : function()
10390     {
10391         this.el.select('tbody', true).first().dom.innerHTML = '';
10392     },
10393     /**
10394      * Show or hide a row.
10395      * @param {Number} rowIndex to show or hide
10396      * @param {Boolean} state hide
10397      */
10398     setRowVisibility : function(rowIndex, state)
10399     {
10400         var bt = this.bodyEl.dom;
10401         
10402         var rows = this.el.select('tbody > tr', true).elements;
10403         
10404         if(typeof(rows[rowIndex]) == 'undefined'){
10405             return;
10406         }
10407         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10408         
10409     },
10410     
10411     
10412     getSelectionModel : function(){
10413         if(!this.selModel){
10414             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10415         }
10416         return this.selModel;
10417     },
10418     /*
10419      * Render the Roo.bootstrap object from renderder
10420      */
10421     renderCellObject : function(r)
10422     {
10423         var _this = this;
10424         
10425         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10426         
10427         var t = r.cfg.render(r.container);
10428         
10429         if(r.cfg.cn){
10430             Roo.each(r.cfg.cn, function(c){
10431                 var child = {
10432                     container: t.getChildContainer(),
10433                     cfg: c
10434                 };
10435                 _this.renderCellObject(child);
10436             })
10437         }
10438     },
10439     /**
10440      * get the Row Index from a dom element.
10441      * @param {Roo.Element} row The row to look for
10442      * @returns {Number} the row
10443      */
10444     getRowIndex : function(row)
10445     {
10446         var rowIndex = -1;
10447         
10448         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10449             if(el != row){
10450                 return;
10451             }
10452             
10453             rowIndex = index;
10454         });
10455         
10456         return rowIndex;
10457     },
10458     /**
10459      * get the header TH element for columnIndex
10460      * @param {Number} columnIndex
10461      * @returns {Roo.Element}
10462      */
10463     getHeaderIndex: function(colIndex)
10464     {
10465         var cols = this.headEl.select('th', true).elements;
10466         return cols[colIndex]; 
10467     },
10468     /**
10469      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10470      * @param {domElement} cell to look for
10471      * @returns {Number} the column
10472      */
10473     getCellIndex : function(cell)
10474     {
10475         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10476         if(id){
10477             return parseInt(id[1], 10);
10478         }
10479         return 0;
10480     },
10481      /**
10482      * Returns the grid's underlying element = used by panel.Grid
10483      * @return {Element} The element
10484      */
10485     getGridEl : function(){
10486         return this.el;
10487     },
10488      /**
10489      * Forces a resize - used by panel.Grid
10490      * @return {Element} The element
10491      */
10492     autoSize : function()
10493     {
10494         //var ctr = Roo.get(this.container.dom.parentElement);
10495         var ctr = Roo.get(this.el.dom);
10496         
10497         var thd = this.getGridEl().select('thead',true).first();
10498         var tbd = this.getGridEl().select('tbody', true).first();
10499         var tfd = this.getGridEl().select('tfoot', true).first();
10500         
10501         var cw = ctr.getWidth();
10502         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10503         
10504         if (tbd) {
10505             
10506             tbd.setWidth(ctr.getWidth());
10507             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10508             // this needs fixing for various usage - currently only hydra job advers I think..
10509             //tdb.setHeight(
10510             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10511             //); 
10512             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10513             cw -= barsize;
10514         }
10515         cw = Math.max(cw, this.totalWidth);
10516         this.getGridEl().select('tbody tr',true).setWidth(cw);
10517         this.initCSS();
10518         
10519         // resize 'expandable coloumn?
10520         
10521         return; // we doe not have a view in this design..
10522         
10523     },
10524     onBodyScroll: function()
10525     {
10526         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10527         if(this.headEl){
10528             this.headEl.setStyle({
10529                 'position' : 'relative',
10530                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10531             });
10532         }
10533         
10534         if(this.lazyLoad){
10535             
10536             var scrollHeight = this.bodyEl.dom.scrollHeight;
10537             
10538             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10539             
10540             var height = this.bodyEl.getHeight();
10541             
10542             if(scrollHeight - height == scrollTop) {
10543                 
10544                 var total = this.ds.getTotalCount();
10545                 
10546                 if(this.footer.cursor + this.footer.pageSize < total){
10547                     
10548                     this.footer.ds.load({
10549                         params : {
10550                             start : this.footer.cursor + this.footer.pageSize,
10551                             limit : this.footer.pageSize
10552                         },
10553                         add : true
10554                     });
10555                 }
10556             }
10557             
10558         }
10559     },
10560     onColumnSplitterMoved : function(i, diff)
10561     {
10562         this.userResized = true;
10563         
10564         var cm = this.colModel;
10565         
10566         var w = this.getHeaderIndex(i).getWidth() + diff;
10567         
10568         
10569         cm.setColumnWidth(i, w, true);
10570         this.initCSS();
10571         //var cid = cm.getColumnId(i); << not used in this version?
10572        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10573         
10574         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10575         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10576         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10577 */
10578         //this.updateSplitters();
10579         //this.layout(); << ??
10580         this.fireEvent("columnresize", i, w);
10581     },
10582     onHeaderChange : function()
10583     {
10584         var header = this.renderHeader();
10585         var table = this.el.select('table', true).first();
10586         
10587         this.headEl.remove();
10588         this.headEl = table.createChild(header, this.bodyEl, false);
10589         
10590         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10591             e.on('click', this.sort, this);
10592         }, this);
10593         
10594         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10595             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10596         }
10597         
10598     },
10599     
10600     onHiddenChange : function(colModel, colIndex, hidden)
10601     {
10602         /*
10603         this.cm.setHidden()
10604         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10605         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10606         
10607         this.CSS.updateRule(thSelector, "display", "");
10608         this.CSS.updateRule(tdSelector, "display", "");
10609         
10610         if(hidden){
10611             this.CSS.updateRule(thSelector, "display", "none");
10612             this.CSS.updateRule(tdSelector, "display", "none");
10613         }
10614         */
10615         // onload calls initCSS()
10616         this.onHeaderChange();
10617         this.onLoad();
10618     },
10619     
10620     setColumnWidth: function(col_index, width)
10621     {
10622         // width = "md-2 xs-2..."
10623         if(!this.colModel.config[col_index]) {
10624             return;
10625         }
10626         
10627         var w = width.split(" ");
10628         
10629         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10630         
10631         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10632         
10633         
10634         for(var j = 0; j < w.length; j++) {
10635             
10636             if(!w[j]) {
10637                 continue;
10638             }
10639             
10640             var size_cls = w[j].split("-");
10641             
10642             if(!Number.isInteger(size_cls[1] * 1)) {
10643                 continue;
10644             }
10645             
10646             if(!this.colModel.config[col_index][size_cls[0]]) {
10647                 continue;
10648             }
10649             
10650             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10651                 continue;
10652             }
10653             
10654             h_row[0].classList.replace(
10655                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10656                 "col-"+size_cls[0]+"-"+size_cls[1]
10657             );
10658             
10659             for(var i = 0; i < rows.length; i++) {
10660                 
10661                 var size_cls = w[j].split("-");
10662                 
10663                 if(!Number.isInteger(size_cls[1] * 1)) {
10664                     continue;
10665                 }
10666                 
10667                 if(!this.colModel.config[col_index][size_cls[0]]) {
10668                     continue;
10669                 }
10670                 
10671                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10672                     continue;
10673                 }
10674                 
10675                 rows[i].classList.replace(
10676                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10677                     "col-"+size_cls[0]+"-"+size_cls[1]
10678                 );
10679             }
10680             
10681             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10682         }
10683     }
10684 });
10685
10686 // currently only used to find the split on drag.. 
10687 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10688
10689 /**
10690  * @depricated
10691 */
10692 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10693 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10694 /*
10695  * - LGPL
10696  *
10697  * table cell
10698  * 
10699  */
10700
10701 /**
10702  * @class Roo.bootstrap.TableCell
10703  * @extends Roo.bootstrap.Component
10704  * @children Roo.bootstrap.Component
10705  * @parent Roo.bootstrap.TableRow
10706  * Bootstrap TableCell class
10707  * 
10708  * @cfg {String} html cell contain text
10709  * @cfg {String} cls cell class
10710  * @cfg {String} tag cell tag (td|th) default td
10711  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10712  * @cfg {String} align Aligns the content in a cell
10713  * @cfg {String} axis Categorizes cells
10714  * @cfg {String} bgcolor Specifies the background color of a cell
10715  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10716  * @cfg {Number} colspan Specifies the number of columns a cell should span
10717  * @cfg {String} headers Specifies one or more header cells a cell is related to
10718  * @cfg {Number} height Sets the height of a cell
10719  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10720  * @cfg {Number} rowspan Sets the number of rows a cell should span
10721  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10722  * @cfg {String} valign Vertical aligns the content in a cell
10723  * @cfg {Number} width Specifies the width of a cell
10724  * 
10725  * @constructor
10726  * Create a new TableCell
10727  * @param {Object} config The config object
10728  */
10729
10730 Roo.bootstrap.TableCell = function(config){
10731     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10732 };
10733
10734 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10735     
10736     html: false,
10737     cls: false,
10738     tag: false,
10739     abbr: false,
10740     align: false,
10741     axis: false,
10742     bgcolor: false,
10743     charoff: false,
10744     colspan: false,
10745     headers: false,
10746     height: false,
10747     nowrap: false,
10748     rowspan: false,
10749     scope: false,
10750     valign: false,
10751     width: false,
10752     
10753     
10754     getAutoCreate : function(){
10755         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10756         
10757         cfg = {
10758             tag: 'td'
10759         };
10760         
10761         if(this.tag){
10762             cfg.tag = this.tag;
10763         }
10764         
10765         if (this.html) {
10766             cfg.html=this.html
10767         }
10768         if (this.cls) {
10769             cfg.cls=this.cls
10770         }
10771         if (this.abbr) {
10772             cfg.abbr=this.abbr
10773         }
10774         if (this.align) {
10775             cfg.align=this.align
10776         }
10777         if (this.axis) {
10778             cfg.axis=this.axis
10779         }
10780         if (this.bgcolor) {
10781             cfg.bgcolor=this.bgcolor
10782         }
10783         if (this.charoff) {
10784             cfg.charoff=this.charoff
10785         }
10786         if (this.colspan) {
10787             cfg.colspan=this.colspan
10788         }
10789         if (this.headers) {
10790             cfg.headers=this.headers
10791         }
10792         if (this.height) {
10793             cfg.height=this.height
10794         }
10795         if (this.nowrap) {
10796             cfg.nowrap=this.nowrap
10797         }
10798         if (this.rowspan) {
10799             cfg.rowspan=this.rowspan
10800         }
10801         if (this.scope) {
10802             cfg.scope=this.scope
10803         }
10804         if (this.valign) {
10805             cfg.valign=this.valign
10806         }
10807         if (this.width) {
10808             cfg.width=this.width
10809         }
10810         
10811         
10812         return cfg;
10813     }
10814    
10815 });
10816
10817  
10818
10819  /*
10820  * - LGPL
10821  *
10822  * table row
10823  * 
10824  */
10825
10826 /**
10827  * @class Roo.bootstrap.TableRow
10828  * @extends Roo.bootstrap.Component
10829  * @children Roo.bootstrap.TableCell
10830  * @parent Roo.bootstrap.TableBody
10831  * Bootstrap TableRow class
10832  * @cfg {String} cls row class
10833  * @cfg {String} align Aligns the content in a table row
10834  * @cfg {String} bgcolor Specifies a background color for a table row
10835  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10836  * @cfg {String} valign Vertical aligns the content in a table row
10837  * 
10838  * @constructor
10839  * Create a new TableRow
10840  * @param {Object} config The config object
10841  */
10842
10843 Roo.bootstrap.TableRow = function(config){
10844     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10845 };
10846
10847 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10848     
10849     cls: false,
10850     align: false,
10851     bgcolor: false,
10852     charoff: false,
10853     valign: false,
10854     
10855     getAutoCreate : function(){
10856         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10857         
10858         cfg = {
10859             tag: 'tr'
10860         };
10861             
10862         if(this.cls){
10863             cfg.cls = this.cls;
10864         }
10865         if(this.align){
10866             cfg.align = this.align;
10867         }
10868         if(this.bgcolor){
10869             cfg.bgcolor = this.bgcolor;
10870         }
10871         if(this.charoff){
10872             cfg.charoff = this.charoff;
10873         }
10874         if(this.valign){
10875             cfg.valign = this.valign;
10876         }
10877         
10878         return cfg;
10879     }
10880    
10881 });
10882
10883  
10884
10885  /*
10886  * - LGPL
10887  *
10888  * table body
10889  * 
10890  */
10891
10892 /**
10893  * @class Roo.bootstrap.TableBody
10894  * @extends Roo.bootstrap.Component
10895  * @children Roo.bootstrap.TableRow
10896  * @parent Roo.bootstrap.Table
10897  * Bootstrap TableBody class
10898  * @cfg {String} cls element class
10899  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10900  * @cfg {String} align Aligns the content inside the element
10901  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10902  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10903  * 
10904  * @constructor
10905  * Create a new TableBody
10906  * @param {Object} config The config object
10907  */
10908
10909 Roo.bootstrap.TableBody = function(config){
10910     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10911 };
10912
10913 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10914     
10915     cls: false,
10916     tag: false,
10917     align: false,
10918     charoff: false,
10919     valign: false,
10920     
10921     getAutoCreate : function(){
10922         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10923         
10924         cfg = {
10925             tag: 'tbody'
10926         };
10927             
10928         if (this.cls) {
10929             cfg.cls=this.cls
10930         }
10931         if(this.tag){
10932             cfg.tag = this.tag;
10933         }
10934         
10935         if(this.align){
10936             cfg.align = this.align;
10937         }
10938         if(this.charoff){
10939             cfg.charoff = this.charoff;
10940         }
10941         if(this.valign){
10942             cfg.valign = this.valign;
10943         }
10944         
10945         return cfg;
10946     }
10947     
10948     
10949 //    initEvents : function()
10950 //    {
10951 //        
10952 //        if(!this.store){
10953 //            return;
10954 //        }
10955 //        
10956 //        this.store = Roo.factory(this.store, Roo.data);
10957 //        this.store.on('load', this.onLoad, this);
10958 //        
10959 //        this.store.load();
10960 //        
10961 //    },
10962 //    
10963 //    onLoad: function () 
10964 //    {   
10965 //        this.fireEvent('load', this);
10966 //    }
10967 //    
10968 //   
10969 });
10970
10971  
10972
10973  /*
10974  * Based on:
10975  * Ext JS Library 1.1.1
10976  * Copyright(c) 2006-2007, Ext JS, LLC.
10977  *
10978  * Originally Released Under LGPL - original licence link has changed is not relivant.
10979  *
10980  * Fork - LGPL
10981  * <script type="text/javascript">
10982  */
10983
10984 // as we use this in bootstrap.
10985 Roo.namespace('Roo.form');
10986  /**
10987  * @class Roo.form.Action
10988  * Internal Class used to handle form actions
10989  * @constructor
10990  * @param {Roo.form.BasicForm} el The form element or its id
10991  * @param {Object} config Configuration options
10992  */
10993
10994  
10995  
10996 // define the action interface
10997 Roo.form.Action = function(form, options){
10998     this.form = form;
10999     this.options = options || {};
11000 };
11001 /**
11002  * Client Validation Failed
11003  * @const 
11004  */
11005 Roo.form.Action.CLIENT_INVALID = 'client';
11006 /**
11007  * Server Validation Failed
11008  * @const 
11009  */
11010 Roo.form.Action.SERVER_INVALID = 'server';
11011  /**
11012  * Connect to Server Failed
11013  * @const 
11014  */
11015 Roo.form.Action.CONNECT_FAILURE = 'connect';
11016 /**
11017  * Reading Data from Server Failed
11018  * @const 
11019  */
11020 Roo.form.Action.LOAD_FAILURE = 'load';
11021
11022 Roo.form.Action.prototype = {
11023     type : 'default',
11024     failureType : undefined,
11025     response : undefined,
11026     result : undefined,
11027
11028     // interface method
11029     run : function(options){
11030
11031     },
11032
11033     // interface method
11034     success : function(response){
11035
11036     },
11037
11038     // interface method
11039     handleResponse : function(response){
11040
11041     },
11042
11043     // default connection failure
11044     failure : function(response){
11045         
11046         this.response = response;
11047         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11048         this.form.afterAction(this, false);
11049     },
11050
11051     processResponse : function(response){
11052         this.response = response;
11053         if(!response.responseText){
11054             return true;
11055         }
11056         this.result = this.handleResponse(response);
11057         return this.result;
11058     },
11059
11060     // utility functions used internally
11061     getUrl : function(appendParams){
11062         var url = this.options.url || this.form.url || this.form.el.dom.action;
11063         if(appendParams){
11064             var p = this.getParams();
11065             if(p){
11066                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11067             }
11068         }
11069         return url;
11070     },
11071
11072     getMethod : function(){
11073         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11074     },
11075
11076     getParams : function(){
11077         var bp = this.form.baseParams;
11078         var p = this.options.params;
11079         if(p){
11080             if(typeof p == "object"){
11081                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11082             }else if(typeof p == 'string' && bp){
11083                 p += '&' + Roo.urlEncode(bp);
11084             }
11085         }else if(bp){
11086             p = Roo.urlEncode(bp);
11087         }
11088         return p;
11089     },
11090
11091     createCallback : function(){
11092         return {
11093             success: this.success,
11094             failure: this.failure,
11095             scope: this,
11096             timeout: (this.form.timeout*1000),
11097             upload: this.form.fileUpload ? this.success : undefined
11098         };
11099     }
11100 };
11101
11102 Roo.form.Action.Submit = function(form, options){
11103     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11104 };
11105
11106 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11107     type : 'submit',
11108
11109     haveProgress : false,
11110     uploadComplete : false,
11111     
11112     // uploadProgress indicator.
11113     uploadProgress : function()
11114     {
11115         if (!this.form.progressUrl) {
11116             return;
11117         }
11118         
11119         if (!this.haveProgress) {
11120             Roo.MessageBox.progress("Uploading", "Uploading");
11121         }
11122         if (this.uploadComplete) {
11123            Roo.MessageBox.hide();
11124            return;
11125         }
11126         
11127         this.haveProgress = true;
11128    
11129         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11130         
11131         var c = new Roo.data.Connection();
11132         c.request({
11133             url : this.form.progressUrl,
11134             params: {
11135                 id : uid
11136             },
11137             method: 'GET',
11138             success : function(req){
11139                //console.log(data);
11140                 var rdata = false;
11141                 var edata;
11142                 try  {
11143                    rdata = Roo.decode(req.responseText)
11144                 } catch (e) {
11145                     Roo.log("Invalid data from server..");
11146                     Roo.log(edata);
11147                     return;
11148                 }
11149                 if (!rdata || !rdata.success) {
11150                     Roo.log(rdata);
11151                     Roo.MessageBox.alert(Roo.encode(rdata));
11152                     return;
11153                 }
11154                 var data = rdata.data;
11155                 
11156                 if (this.uploadComplete) {
11157                    Roo.MessageBox.hide();
11158                    return;
11159                 }
11160                    
11161                 if (data){
11162                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11163                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11164                     );
11165                 }
11166                 this.uploadProgress.defer(2000,this);
11167             },
11168        
11169             failure: function(data) {
11170                 Roo.log('progress url failed ');
11171                 Roo.log(data);
11172             },
11173             scope : this
11174         });
11175            
11176     },
11177     
11178     
11179     run : function()
11180     {
11181         // run get Values on the form, so it syncs any secondary forms.
11182         this.form.getValues();
11183         
11184         var o = this.options;
11185         var method = this.getMethod();
11186         var isPost = method == 'POST';
11187         if(o.clientValidation === false || this.form.isValid()){
11188             
11189             if (this.form.progressUrl) {
11190                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11191                     (new Date() * 1) + '' + Math.random());
11192                     
11193             } 
11194             
11195             
11196             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11197                 form:this.form.el.dom,
11198                 url:this.getUrl(!isPost),
11199                 method: method,
11200                 params:isPost ? this.getParams() : null,
11201                 isUpload: this.form.fileUpload,
11202                 formData : this.form.formData
11203             }));
11204             
11205             this.uploadProgress();
11206
11207         }else if (o.clientValidation !== false){ // client validation failed
11208             this.failureType = Roo.form.Action.CLIENT_INVALID;
11209             this.form.afterAction(this, false);
11210         }
11211     },
11212
11213     success : function(response)
11214     {
11215         this.uploadComplete= true;
11216         if (this.haveProgress) {
11217             Roo.MessageBox.hide();
11218         }
11219         
11220         
11221         var result = this.processResponse(response);
11222         if(result === true || result.success){
11223             this.form.afterAction(this, true);
11224             return;
11225         }
11226         if(result.errors){
11227             this.form.markInvalid(result.errors);
11228             this.failureType = Roo.form.Action.SERVER_INVALID;
11229         }
11230         this.form.afterAction(this, false);
11231     },
11232     failure : function(response)
11233     {
11234         this.uploadComplete= true;
11235         if (this.haveProgress) {
11236             Roo.MessageBox.hide();
11237         }
11238         
11239         this.response = response;
11240         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11241         this.form.afterAction(this, false);
11242     },
11243     
11244     handleResponse : function(response){
11245         if(this.form.errorReader){
11246             var rs = this.form.errorReader.read(response);
11247             var errors = [];
11248             if(rs.records){
11249                 for(var i = 0, len = rs.records.length; i < len; i++) {
11250                     var r = rs.records[i];
11251                     errors[i] = r.data;
11252                 }
11253             }
11254             if(errors.length < 1){
11255                 errors = null;
11256             }
11257             return {
11258                 success : rs.success,
11259                 errors : errors
11260             };
11261         }
11262         var ret = false;
11263         try {
11264             ret = Roo.decode(response.responseText);
11265         } catch (e) {
11266             ret = {
11267                 success: false,
11268                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11269                 errors : []
11270             };
11271         }
11272         return ret;
11273         
11274     }
11275 });
11276
11277
11278 Roo.form.Action.Load = function(form, options){
11279     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11280     this.reader = this.form.reader;
11281 };
11282
11283 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11284     type : 'load',
11285
11286     run : function(){
11287         
11288         Roo.Ajax.request(Roo.apply(
11289                 this.createCallback(), {
11290                     method:this.getMethod(),
11291                     url:this.getUrl(false),
11292                     params:this.getParams()
11293         }));
11294     },
11295
11296     success : function(response){
11297         
11298         var result = this.processResponse(response);
11299         if(result === true || !result.success || !result.data){
11300             this.failureType = Roo.form.Action.LOAD_FAILURE;
11301             this.form.afterAction(this, false);
11302             return;
11303         }
11304         this.form.clearInvalid();
11305         this.form.setValues(result.data);
11306         this.form.afterAction(this, true);
11307     },
11308
11309     handleResponse : function(response){
11310         if(this.form.reader){
11311             var rs = this.form.reader.read(response);
11312             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11313             return {
11314                 success : rs.success,
11315                 data : data
11316             };
11317         }
11318         return Roo.decode(response.responseText);
11319     }
11320 });
11321
11322 Roo.form.Action.ACTION_TYPES = {
11323     'load' : Roo.form.Action.Load,
11324     'submit' : Roo.form.Action.Submit
11325 };/*
11326  * - LGPL
11327  *
11328  * form
11329  *
11330  */
11331
11332 /**
11333  * @class Roo.bootstrap.form.Form
11334  * @extends Roo.bootstrap.Component
11335  * @children Roo.bootstrap.Component
11336  * Bootstrap Form class
11337  * @cfg {String} method  GET | POST (default POST)
11338  * @cfg {String} labelAlign top | left (default top)
11339  * @cfg {String} align left  | right - for navbars
11340  * @cfg {Boolean} loadMask load mask when submit (default true)
11341
11342  *
11343  * @constructor
11344  * Create a new Form
11345  * @param {Object} config The config object
11346  */
11347
11348
11349 Roo.bootstrap.form.Form = function(config){
11350     
11351     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11352     
11353     Roo.bootstrap.form.Form.popover.apply();
11354     
11355     this.addEvents({
11356         /**
11357          * @event clientvalidation
11358          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11359          * @param {Form} this
11360          * @param {Boolean} valid true if the form has passed client-side validation
11361          */
11362         clientvalidation: true,
11363         /**
11364          * @event beforeaction
11365          * Fires before any action is performed. Return false to cancel the action.
11366          * @param {Form} this
11367          * @param {Action} action The action to be performed
11368          */
11369         beforeaction: true,
11370         /**
11371          * @event actionfailed
11372          * Fires when an action fails.
11373          * @param {Form} this
11374          * @param {Action} action The action that failed
11375          */
11376         actionfailed : true,
11377         /**
11378          * @event actioncomplete
11379          * Fires when an action is completed.
11380          * @param {Form} this
11381          * @param {Action} action The action that completed
11382          */
11383         actioncomplete : true
11384     });
11385 };
11386
11387 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11388
11389      /**
11390      * @cfg {String} method
11391      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11392      */
11393     method : 'POST',
11394     /**
11395      * @cfg {String} url
11396      * The URL to use for form actions if one isn't supplied in the action options.
11397      */
11398     /**
11399      * @cfg {Boolean} fileUpload
11400      * Set to true if this form is a file upload.
11401      */
11402
11403     /**
11404      * @cfg {Object} baseParams
11405      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11406      */
11407
11408     /**
11409      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11410      */
11411     timeout: 30,
11412     /**
11413      * @cfg {Sting} align (left|right) for navbar forms
11414      */
11415     align : 'left',
11416
11417     // private
11418     activeAction : null,
11419
11420     /**
11421      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11422      * element by passing it or its id or mask the form itself by passing in true.
11423      * @type Mixed
11424      */
11425     waitMsgTarget : false,
11426
11427     loadMask : true,
11428     
11429     /**
11430      * @cfg {Boolean} errorMask (true|false) default false
11431      */
11432     errorMask : false,
11433     
11434     /**
11435      * @cfg {Number} maskOffset Default 100
11436      */
11437     maskOffset : 100,
11438     
11439     /**
11440      * @cfg {Boolean} maskBody
11441      */
11442     maskBody : false,
11443
11444     getAutoCreate : function(){
11445
11446         var cfg = {
11447             tag: 'form',
11448             method : this.method || 'POST',
11449             id : this.id || Roo.id(),
11450             cls : ''
11451         };
11452         if (this.parent().xtype.match(/^Nav/)) {
11453             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11454
11455         }
11456
11457         if (this.labelAlign == 'left' ) {
11458             cfg.cls += ' form-horizontal';
11459         }
11460
11461
11462         return cfg;
11463     },
11464     initEvents : function()
11465     {
11466         this.el.on('submit', this.onSubmit, this);
11467         // this was added as random key presses on the form where triggering form submit.
11468         this.el.on('keypress', function(e) {
11469             if (e.getCharCode() != 13) {
11470                 return true;
11471             }
11472             // we might need to allow it for textareas.. and some other items.
11473             // check e.getTarget().
11474
11475             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11476                 return true;
11477             }
11478
11479             Roo.log("keypress blocked");
11480
11481             e.preventDefault();
11482             return false;
11483         });
11484         
11485     },
11486     // private
11487     onSubmit : function(e){
11488         e.stopEvent();
11489     },
11490
11491      /**
11492      * Returns true if client-side validation on the form is successful.
11493      * @return Boolean
11494      */
11495     isValid : function(){
11496         var items = this.getItems();
11497         var valid = true;
11498         var target = false;
11499         
11500         items.each(function(f){
11501             
11502             if(f.validate()){
11503                 return;
11504             }
11505             
11506             Roo.log('invalid field: ' + f.name);
11507             
11508             valid = false;
11509
11510             if(!target && f.el.isVisible(true)){
11511                 target = f;
11512             }
11513            
11514         });
11515         
11516         if(this.errorMask && !valid){
11517             Roo.bootstrap.form.Form.popover.mask(this, target);
11518         }
11519         
11520         return valid;
11521     },
11522     
11523     /**
11524      * Returns true if any fields in this form have changed since their original load.
11525      * @return Boolean
11526      */
11527     isDirty : function(){
11528         var dirty = false;
11529         var items = this.getItems();
11530         items.each(function(f){
11531            if(f.isDirty()){
11532                dirty = true;
11533                return false;
11534            }
11535            return true;
11536         });
11537         return dirty;
11538     },
11539      /**
11540      * Performs a predefined action (submit or load) or custom actions you define on this form.
11541      * @param {String} actionName The name of the action type
11542      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11543      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11544      * accept other config options):
11545      * <pre>
11546 Property          Type             Description
11547 ----------------  ---------------  ----------------------------------------------------------------------------------
11548 url               String           The url for the action (defaults to the form's url)
11549 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11550 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11551 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11552                                    validate the form on the client (defaults to false)
11553      * </pre>
11554      * @return {BasicForm} this
11555      */
11556     doAction : function(action, options){
11557         if(typeof action == 'string'){
11558             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11559         }
11560         if(this.fireEvent('beforeaction', this, action) !== false){
11561             this.beforeAction(action);
11562             action.run.defer(100, action);
11563         }
11564         return this;
11565     },
11566
11567     // private
11568     beforeAction : function(action){
11569         var o = action.options;
11570         
11571         if(this.loadMask){
11572             
11573             if(this.maskBody){
11574                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11575             } else {
11576                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11577             }
11578         }
11579         // not really supported yet.. ??
11580
11581         //if(this.waitMsgTarget === true){
11582         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11583         //}else if(this.waitMsgTarget){
11584         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11585         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11586         //}else {
11587         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11588        // }
11589
11590     },
11591
11592     // private
11593     afterAction : function(action, success){
11594         this.activeAction = null;
11595         var o = action.options;
11596
11597         if(this.loadMask){
11598             
11599             if(this.maskBody){
11600                 Roo.get(document.body).unmask();
11601             } else {
11602                 this.el.unmask();
11603             }
11604         }
11605         
11606         //if(this.waitMsgTarget === true){
11607 //            this.el.unmask();
11608         //}else if(this.waitMsgTarget){
11609         //    this.waitMsgTarget.unmask();
11610         //}else{
11611         //    Roo.MessageBox.updateProgress(1);
11612         //    Roo.MessageBox.hide();
11613        // }
11614         //
11615         if(success){
11616             if(o.reset){
11617                 this.reset();
11618             }
11619             Roo.callback(o.success, o.scope, [this, action]);
11620             this.fireEvent('actioncomplete', this, action);
11621
11622         }else{
11623
11624             // failure condition..
11625             // we have a scenario where updates need confirming.
11626             // eg. if a locking scenario exists..
11627             // we look for { errors : { needs_confirm : true }} in the response.
11628             if (
11629                 (typeof(action.result) != 'undefined')  &&
11630                 (typeof(action.result.errors) != 'undefined')  &&
11631                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11632            ){
11633                 var _t = this;
11634                 Roo.log("not supported yet");
11635                  /*
11636
11637                 Roo.MessageBox.confirm(
11638                     "Change requires confirmation",
11639                     action.result.errorMsg,
11640                     function(r) {
11641                         if (r != 'yes') {
11642                             return;
11643                         }
11644                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11645                     }
11646
11647                 );
11648                 */
11649
11650
11651                 return;
11652             }
11653
11654             Roo.callback(o.failure, o.scope, [this, action]);
11655             // show an error message if no failed handler is set..
11656             if (!this.hasListener('actionfailed')) {
11657                 Roo.log("need to add dialog support");
11658                 /*
11659                 Roo.MessageBox.alert("Error",
11660                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11661                         action.result.errorMsg :
11662                         "Saving Failed, please check your entries or try again"
11663                 );
11664                 */
11665             }
11666
11667             this.fireEvent('actionfailed', this, action);
11668         }
11669
11670     },
11671     /**
11672      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11673      * @param {String} id The value to search for
11674      * @return Field
11675      */
11676     findField : function(id){
11677         var items = this.getItems();
11678         var field = items.get(id);
11679         if(!field){
11680              items.each(function(f){
11681                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11682                     field = f;
11683                     return false;
11684                 }
11685                 return true;
11686             });
11687         }
11688         return field || null;
11689     },
11690      /**
11691      * Mark fields in this form invalid in bulk.
11692      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11693      * @return {BasicForm} this
11694      */
11695     markInvalid : function(errors){
11696         if(errors instanceof Array){
11697             for(var i = 0, len = errors.length; i < len; i++){
11698                 var fieldError = errors[i];
11699                 var f = this.findField(fieldError.id);
11700                 if(f){
11701                     f.markInvalid(fieldError.msg);
11702                 }
11703             }
11704         }else{
11705             var field, id;
11706             for(id in errors){
11707                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11708                     field.markInvalid(errors[id]);
11709                 }
11710             }
11711         }
11712         //Roo.each(this.childForms || [], function (f) {
11713         //    f.markInvalid(errors);
11714         //});
11715
11716         return this;
11717     },
11718
11719     /**
11720      * Set values for fields in this form in bulk.
11721      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11722      * @return {BasicForm} this
11723      */
11724     setValues : function(values){
11725         if(values instanceof Array){ // array of objects
11726             for(var i = 0, len = values.length; i < len; i++){
11727                 var v = values[i];
11728                 var f = this.findField(v.id);
11729                 if(f){
11730                     f.setValue(v.value);
11731                     if(this.trackResetOnLoad){
11732                         f.originalValue = f.getValue();
11733                     }
11734                 }
11735             }
11736         }else{ // object hash
11737             var field, id;
11738             for(id in values){
11739                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11740
11741                     if (field.setFromData &&
11742                         field.valueField &&
11743                         field.displayField &&
11744                         // combos' with local stores can
11745                         // be queried via setValue()
11746                         // to set their value..
11747                         (field.store && !field.store.isLocal)
11748                         ) {
11749                         // it's a combo
11750                         var sd = { };
11751                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11752                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11753                         field.setFromData(sd);
11754
11755                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11756                         
11757                         field.setFromData(values);
11758                         
11759                     } else {
11760                         field.setValue(values[id]);
11761                     }
11762
11763
11764                     if(this.trackResetOnLoad){
11765                         field.originalValue = field.getValue();
11766                     }
11767                 }
11768             }
11769         }
11770
11771         //Roo.each(this.childForms || [], function (f) {
11772         //    f.setValues(values);
11773         //});
11774
11775         return this;
11776     },
11777
11778     /**
11779      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11780      * they are returned as an array.
11781      * @param {Boolean} asString
11782      * @return {Object}
11783      */
11784     getValues : function(asString){
11785         //if (this.childForms) {
11786             // copy values from the child forms
11787         //    Roo.each(this.childForms, function (f) {
11788         //        this.setValues(f.getValues());
11789         //    }, this);
11790         //}
11791
11792
11793
11794         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11795         if(asString === true){
11796             return fs;
11797         }
11798         return Roo.urlDecode(fs);
11799     },
11800
11801     /**
11802      * Returns the fields in this form as an object with key/value pairs.
11803      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11804      * @return {Object}
11805      */
11806     getFieldValues : function(with_hidden)
11807     {
11808         var items = this.getItems();
11809         var ret = {};
11810         items.each(function(f){
11811             
11812             if (!f.getName()) {
11813                 return;
11814             }
11815             
11816             var v = f.getValue();
11817             
11818             if (f.inputType =='radio') {
11819                 if (typeof(ret[f.getName()]) == 'undefined') {
11820                     ret[f.getName()] = ''; // empty..
11821                 }
11822
11823                 if (!f.el.dom.checked) {
11824                     return;
11825
11826                 }
11827                 v = f.el.dom.value;
11828
11829             }
11830             
11831             if(f.xtype == 'MoneyField'){
11832                 ret[f.currencyName] = f.getCurrency();
11833             }
11834
11835             // not sure if this supported any more..
11836             if ((typeof(v) == 'object') && f.getRawValue) {
11837                 v = f.getRawValue() ; // dates..
11838             }
11839             // combo boxes where name != hiddenName...
11840             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11841                 ret[f.name] = f.getRawValue();
11842             }
11843             ret[f.getName()] = v;
11844         });
11845
11846         return ret;
11847     },
11848
11849     /**
11850      * Clears all invalid messages in this form.
11851      * @return {BasicForm} this
11852      */
11853     clearInvalid : function(){
11854         var items = this.getItems();
11855
11856         items.each(function(f){
11857            f.clearInvalid();
11858         });
11859
11860         return this;
11861     },
11862
11863     /**
11864      * Resets this form.
11865      * @return {BasicForm} this
11866      */
11867     reset : function(){
11868         var items = this.getItems();
11869         items.each(function(f){
11870             f.reset();
11871         });
11872
11873         Roo.each(this.childForms || [], function (f) {
11874             f.reset();
11875         });
11876
11877
11878         return this;
11879     },
11880     
11881     getItems : function()
11882     {
11883         var r=new Roo.util.MixedCollection(false, function(o){
11884             return o.id || (o.id = Roo.id());
11885         });
11886         var iter = function(el) {
11887             if (el.inputEl) {
11888                 r.add(el);
11889             }
11890             if (!el.items) {
11891                 return;
11892             }
11893             Roo.each(el.items,function(e) {
11894                 iter(e);
11895             });
11896         };
11897
11898         iter(this);
11899         return r;
11900     },
11901     
11902     hideFields : function(items)
11903     {
11904         Roo.each(items, function(i){
11905             
11906             var f = this.findField(i);
11907             
11908             if(!f){
11909                 return;
11910             }
11911             
11912             f.hide();
11913             
11914         }, this);
11915     },
11916     
11917     showFields : function(items)
11918     {
11919         Roo.each(items, function(i){
11920             
11921             var f = this.findField(i);
11922             
11923             if(!f){
11924                 return;
11925             }
11926             
11927             f.show();
11928             
11929         }, this);
11930     }
11931
11932 });
11933
11934 Roo.apply(Roo.bootstrap.form.Form, {
11935     
11936     popover : {
11937         
11938         padding : 5,
11939         
11940         isApplied : false,
11941         
11942         isMasked : false,
11943         
11944         form : false,
11945         
11946         target : false,
11947         
11948         toolTip : false,
11949         
11950         intervalID : false,
11951         
11952         maskEl : false,
11953         
11954         apply : function()
11955         {
11956             if(this.isApplied){
11957                 return;
11958             }
11959             
11960             this.maskEl = {
11961                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11962                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11963                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11964                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11965             };
11966             
11967             this.maskEl.top.enableDisplayMode("block");
11968             this.maskEl.left.enableDisplayMode("block");
11969             this.maskEl.bottom.enableDisplayMode("block");
11970             this.maskEl.right.enableDisplayMode("block");
11971             
11972             this.toolTip = new Roo.bootstrap.Tooltip({
11973                 cls : 'roo-form-error-popover',
11974                 alignment : {
11975                     'left' : ['r-l', [-2,0], 'right'],
11976                     'right' : ['l-r', [2,0], 'left'],
11977                     'bottom' : ['tl-bl', [0,2], 'top'],
11978                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11979                 }
11980             });
11981             
11982             this.toolTip.render(Roo.get(document.body));
11983
11984             this.toolTip.el.enableDisplayMode("block");
11985             
11986             Roo.get(document.body).on('click', function(){
11987                 this.unmask();
11988             }, this);
11989             
11990             Roo.get(document.body).on('touchstart', function(){
11991                 this.unmask();
11992             }, this);
11993             
11994             this.isApplied = true
11995         },
11996         
11997         mask : function(form, target)
11998         {
11999             this.form = form;
12000             
12001             this.target = target;
12002             
12003             if(!this.form.errorMask || !target.el){
12004                 return;
12005             }
12006             
12007             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12008             
12009             Roo.log(scrollable);
12010             
12011             var ot = this.target.el.calcOffsetsTo(scrollable);
12012             
12013             var scrollTo = ot[1] - this.form.maskOffset;
12014             
12015             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12016             
12017             scrollable.scrollTo('top', scrollTo);
12018             
12019             var box = this.target.el.getBox();
12020             Roo.log(box);
12021             var zIndex = Roo.bootstrap.Modal.zIndex++;
12022
12023             
12024             this.maskEl.top.setStyle('position', 'absolute');
12025             this.maskEl.top.setStyle('z-index', zIndex);
12026             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12027             this.maskEl.top.setLeft(0);
12028             this.maskEl.top.setTop(0);
12029             this.maskEl.top.show();
12030             
12031             this.maskEl.left.setStyle('position', 'absolute');
12032             this.maskEl.left.setStyle('z-index', zIndex);
12033             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12034             this.maskEl.left.setLeft(0);
12035             this.maskEl.left.setTop(box.y - this.padding);
12036             this.maskEl.left.show();
12037
12038             this.maskEl.bottom.setStyle('position', 'absolute');
12039             this.maskEl.bottom.setStyle('z-index', zIndex);
12040             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12041             this.maskEl.bottom.setLeft(0);
12042             this.maskEl.bottom.setTop(box.bottom + this.padding);
12043             this.maskEl.bottom.show();
12044
12045             this.maskEl.right.setStyle('position', 'absolute');
12046             this.maskEl.right.setStyle('z-index', zIndex);
12047             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12048             this.maskEl.right.setLeft(box.right + this.padding);
12049             this.maskEl.right.setTop(box.y - this.padding);
12050             this.maskEl.right.show();
12051
12052             this.toolTip.bindEl = this.target.el;
12053
12054             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12055
12056             var tip = this.target.blankText;
12057
12058             if(this.target.getValue() !== '' ) {
12059                 
12060                 if (this.target.invalidText.length) {
12061                     tip = this.target.invalidText;
12062                 } else if (this.target.regexText.length){
12063                     tip = this.target.regexText;
12064                 }
12065             }
12066
12067             this.toolTip.show(tip);
12068
12069             this.intervalID = window.setInterval(function() {
12070                 Roo.bootstrap.form.Form.popover.unmask();
12071             }, 10000);
12072
12073             window.onwheel = function(){ return false;};
12074             
12075             (function(){ this.isMasked = true; }).defer(500, this);
12076             
12077         },
12078         
12079         unmask : function()
12080         {
12081             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12082                 return;
12083             }
12084             
12085             this.maskEl.top.setStyle('position', 'absolute');
12086             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12087             this.maskEl.top.hide();
12088
12089             this.maskEl.left.setStyle('position', 'absolute');
12090             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12091             this.maskEl.left.hide();
12092
12093             this.maskEl.bottom.setStyle('position', 'absolute');
12094             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12095             this.maskEl.bottom.hide();
12096
12097             this.maskEl.right.setStyle('position', 'absolute');
12098             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12099             this.maskEl.right.hide();
12100             
12101             this.toolTip.hide();
12102             
12103             this.toolTip.el.hide();
12104             
12105             window.onwheel = function(){ return true;};
12106             
12107             if(this.intervalID){
12108                 window.clearInterval(this.intervalID);
12109                 this.intervalID = false;
12110             }
12111             
12112             this.isMasked = false;
12113             
12114         }
12115         
12116     }
12117     
12118 });
12119
12120 /*
12121  * Based on:
12122  * Ext JS Library 1.1.1
12123  * Copyright(c) 2006-2007, Ext JS, LLC.
12124  *
12125  * Originally Released Under LGPL - original licence link has changed is not relivant.
12126  *
12127  * Fork - LGPL
12128  * <script type="text/javascript">
12129  */
12130 /**
12131  * @class Roo.form.VTypes
12132  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12133  * @static
12134  */
12135 Roo.form.VTypes = function(){
12136     // closure these in so they are only created once.
12137     var alpha = /^[a-zA-Z_]+$/;
12138     var alphanum = /^[a-zA-Z0-9_]+$/;
12139     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12140     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12141
12142     // All these messages and functions are configurable
12143     return {
12144         /**
12145          * The function used to validate email addresses
12146          * @param {String} value The email address
12147          */
12148         'email' : function(v){
12149             return email.test(v);
12150         },
12151         /**
12152          * The error text to display when the email validation function returns false
12153          * @type String
12154          */
12155         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12156         /**
12157          * The keystroke filter mask to be applied on email input
12158          * @type RegExp
12159          */
12160         'emailMask' : /[a-z0-9_\.\-@]/i,
12161
12162         /**
12163          * The function used to validate URLs
12164          * @param {String} value The URL
12165          */
12166         'url' : function(v){
12167             return url.test(v);
12168         },
12169         /**
12170          * The error text to display when the url validation function returns false
12171          * @type String
12172          */
12173         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12174         
12175         /**
12176          * The function used to validate alpha values
12177          * @param {String} value The value
12178          */
12179         'alpha' : function(v){
12180             return alpha.test(v);
12181         },
12182         /**
12183          * The error text to display when the alpha validation function returns false
12184          * @type String
12185          */
12186         'alphaText' : 'This field should only contain letters and _',
12187         /**
12188          * The keystroke filter mask to be applied on alpha input
12189          * @type RegExp
12190          */
12191         'alphaMask' : /[a-z_]/i,
12192
12193         /**
12194          * The function used to validate alphanumeric values
12195          * @param {String} value The value
12196          */
12197         'alphanum' : function(v){
12198             return alphanum.test(v);
12199         },
12200         /**
12201          * The error text to display when the alphanumeric validation function returns false
12202          * @type String
12203          */
12204         'alphanumText' : 'This field should only contain letters, numbers and _',
12205         /**
12206          * The keystroke filter mask to be applied on alphanumeric input
12207          * @type RegExp
12208          */
12209         'alphanumMask' : /[a-z0-9_]/i
12210     };
12211 }();/*
12212  * - LGPL
12213  *
12214  * Input
12215  * 
12216  */
12217
12218 /**
12219  * @class Roo.bootstrap.form.Input
12220  * @extends Roo.bootstrap.Component
12221  * Bootstrap Input class
12222  * @cfg {Boolean} disabled is it disabled
12223  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12224  * @cfg {String} name name of the input
12225  * @cfg {string} fieldLabel - the label associated
12226  * @cfg {string} placeholder - placeholder to put in text.
12227  * @cfg {string} before - input group add on before
12228  * @cfg {string} after - input group add on after
12229  * @cfg {string} size - (lg|sm) or leave empty..
12230  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12231  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12232  * @cfg {Number} md colspan out of 12 for computer-sized screens
12233  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12234  * @cfg {string} value default value of the input
12235  * @cfg {Number} labelWidth set the width of label 
12236  * @cfg {Number} labellg set the width of label (1-12)
12237  * @cfg {Number} labelmd set the width of label (1-12)
12238  * @cfg {Number} labelsm set the width of label (1-12)
12239  * @cfg {Number} labelxs set the width of label (1-12)
12240  * @cfg {String} labelAlign (top|left)
12241  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12242  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12243  * @cfg {String} indicatorpos (left|right) default left
12244  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12245  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12246  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12247  * @cfg {Roo.bootstrap.Button} before Button to show before
12248  * @cfg {Roo.bootstrap.Button} afterButton to show before
12249  * @cfg {String} align (left|center|right) Default left
12250  * @cfg {Boolean} forceFeedback (true|false) Default false
12251  * 
12252  * @constructor
12253  * Create a new Input
12254  * @param {Object} config The config object
12255  */
12256
12257 Roo.bootstrap.form.Input = function(config){
12258     
12259     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12260     
12261     this.addEvents({
12262         /**
12263          * @event focus
12264          * Fires when this field receives input focus.
12265          * @param {Roo.form.Field} this
12266          */
12267         focus : true,
12268         /**
12269          * @event blur
12270          * Fires when this field loses input focus.
12271          * @param {Roo.form.Field} this
12272          */
12273         blur : true,
12274         /**
12275          * @event specialkey
12276          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12277          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12278          * @param {Roo.form.Field} this
12279          * @param {Roo.EventObject} e The event object
12280          */
12281         specialkey : true,
12282         /**
12283          * @event change
12284          * Fires just before the field blurs if the field value has changed.
12285          * @param {Roo.form.Field} this
12286          * @param {Mixed} newValue The new value
12287          * @param {Mixed} oldValue The original value
12288          */
12289         change : true,
12290         /**
12291          * @event invalid
12292          * Fires after the field has been marked as invalid.
12293          * @param {Roo.form.Field} this
12294          * @param {String} msg The validation message
12295          */
12296         invalid : true,
12297         /**
12298          * @event valid
12299          * Fires after the field has been validated with no errors.
12300          * @param {Roo.form.Field} this
12301          */
12302         valid : true,
12303          /**
12304          * @event keyup
12305          * Fires after the key up
12306          * @param {Roo.form.Field} this
12307          * @param {Roo.EventObject}  e The event Object
12308          */
12309         keyup : true,
12310         /**
12311          * @event paste
12312          * Fires after the user pastes into input
12313          * @param {Roo.form.Field} this
12314          * @param {Roo.EventObject}  e The event Object
12315          */
12316         paste : true
12317     });
12318 };
12319
12320 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12321      /**
12322      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12323       automatic validation (defaults to "keyup").
12324      */
12325     validationEvent : "keyup",
12326      /**
12327      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12328      */
12329     validateOnBlur : true,
12330     /**
12331      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12332      */
12333     validationDelay : 250,
12334      /**
12335      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12336      */
12337     focusClass : "x-form-focus",  // not needed???
12338     
12339        
12340     /**
12341      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12342      */
12343     invalidClass : "has-warning",
12344     
12345     /**
12346      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12347      */
12348     validClass : "has-success",
12349     
12350     /**
12351      * @cfg {Boolean} hasFeedback (true|false) default true
12352      */
12353     hasFeedback : true,
12354     
12355     /**
12356      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12357      */
12358     invalidFeedbackClass : "glyphicon-warning-sign",
12359     
12360     /**
12361      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12362      */
12363     validFeedbackClass : "glyphicon-ok",
12364     
12365     /**
12366      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12367      */
12368     selectOnFocus : false,
12369     
12370      /**
12371      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12372      */
12373     maskRe : null,
12374        /**
12375      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12376      */
12377     vtype : null,
12378     
12379       /**
12380      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12381      */
12382     disableKeyFilter : false,
12383     
12384        /**
12385      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12386      */
12387     disabled : false,
12388      /**
12389      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12390      */
12391     allowBlank : true,
12392     /**
12393      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12394      */
12395     blankText : "Please complete this mandatory field",
12396     
12397      /**
12398      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12399      */
12400     minLength : 0,
12401     /**
12402      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12403      */
12404     maxLength : Number.MAX_VALUE,
12405     /**
12406      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12407      */
12408     minLengthText : "The minimum length for this field is {0}",
12409     /**
12410      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12411      */
12412     maxLengthText : "The maximum length for this field is {0}",
12413   
12414     
12415     /**
12416      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12417      * If available, this function will be called only after the basic validators all return true, and will be passed the
12418      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12419      */
12420     validator : null,
12421     /**
12422      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12423      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12424      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12425      */
12426     regex : null,
12427     /**
12428      * @cfg {String} regexText -- Depricated - use Invalid Text
12429      */
12430     regexText : "",
12431     
12432     /**
12433      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12434      */
12435     invalidText : "",
12436     
12437     
12438     
12439     autocomplete: false,
12440     
12441     
12442     fieldLabel : '',
12443     inputType : 'text',
12444     
12445     name : false,
12446     placeholder: false,
12447     before : false,
12448     after : false,
12449     size : false,
12450     hasFocus : false,
12451     preventMark: false,
12452     isFormField : true,
12453     value : '',
12454     labelWidth : 2,
12455     labelAlign : false,
12456     readOnly : false,
12457     align : false,
12458     formatedValue : false,
12459     forceFeedback : false,
12460     
12461     indicatorpos : 'left',
12462     
12463     labellg : 0,
12464     labelmd : 0,
12465     labelsm : 0,
12466     labelxs : 0,
12467     
12468     capture : '',
12469     accept : '',
12470     
12471     parentLabelAlign : function()
12472     {
12473         var parent = this;
12474         while (parent.parent()) {
12475             parent = parent.parent();
12476             if (typeof(parent.labelAlign) !='undefined') {
12477                 return parent.labelAlign;
12478             }
12479         }
12480         return 'left';
12481         
12482     },
12483     
12484     getAutoCreate : function()
12485     {
12486         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12487         
12488         var id = Roo.id();
12489         
12490         var cfg = {};
12491         
12492         if(this.inputType != 'hidden'){
12493             cfg.cls = 'form-group' //input-group
12494         }
12495         
12496         var input =  {
12497             tag: 'input',
12498             id : id,
12499             type : this.inputType,
12500             value : this.value,
12501             cls : 'form-control',
12502             placeholder : this.placeholder || '',
12503             autocomplete : this.autocomplete || 'new-password'
12504         };
12505         if (this.inputType == 'file') {
12506             input.style = 'overflow:hidden'; // why not in CSS?
12507         }
12508         
12509         if(this.capture.length){
12510             input.capture = this.capture;
12511         }
12512         
12513         if(this.accept.length){
12514             input.accept = this.accept + "/*";
12515         }
12516         
12517         if(this.align){
12518             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12519         }
12520         
12521         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12522             input.maxLength = this.maxLength;
12523         }
12524         
12525         if (this.disabled) {
12526             input.disabled=true;
12527         }
12528         
12529         if (this.readOnly) {
12530             input.readonly=true;
12531         }
12532         
12533         if (this.name) {
12534             input.name = this.name;
12535         }
12536         
12537         if (this.size) {
12538             input.cls += ' input-' + this.size;
12539         }
12540         
12541         var settings=this;
12542         ['xs','sm','md','lg'].map(function(size){
12543             if (settings[size]) {
12544                 cfg.cls += ' col-' + size + '-' + settings[size];
12545             }
12546         });
12547         
12548         var inputblock = input;
12549         
12550         var feedback = {
12551             tag: 'span',
12552             cls: 'glyphicon form-control-feedback'
12553         };
12554             
12555         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12556             
12557             inputblock = {
12558                 cls : 'has-feedback',
12559                 cn :  [
12560                     input,
12561                     feedback
12562                 ] 
12563             };  
12564         }
12565         
12566         if (this.before || this.after) {
12567             
12568             inputblock = {
12569                 cls : 'input-group',
12570                 cn :  [] 
12571             };
12572             
12573             if (this.before && typeof(this.before) == 'string') {
12574                 
12575                 inputblock.cn.push({
12576                     tag :'span',
12577                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12578                     html : this.before
12579                 });
12580             }
12581             if (this.before && typeof(this.before) == 'object') {
12582                 this.before = Roo.factory(this.before);
12583                 
12584                 inputblock.cn.push({
12585                     tag :'span',
12586                     cls : 'roo-input-before input-group-prepend   input-group-' +
12587                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12588                 });
12589             }
12590             
12591             inputblock.cn.push(input);
12592             
12593             if (this.after && typeof(this.after) == 'string') {
12594                 inputblock.cn.push({
12595                     tag :'span',
12596                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12597                     html : this.after
12598                 });
12599             }
12600             if (this.after && typeof(this.after) == 'object') {
12601                 this.after = Roo.factory(this.after);
12602                 
12603                 inputblock.cn.push({
12604                     tag :'span',
12605                     cls : 'roo-input-after input-group-append  input-group-' +
12606                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12607                 });
12608             }
12609             
12610             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12611                 inputblock.cls += ' has-feedback';
12612                 inputblock.cn.push(feedback);
12613             }
12614         };
12615         var indicator = {
12616             tag : 'i',
12617             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12618             tooltip : 'This field is required'
12619         };
12620         if (this.allowBlank ) {
12621             indicator.style = this.allowBlank ? ' display:none' : '';
12622         }
12623         if (align ==='left' && this.fieldLabel.length) {
12624             
12625             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12626             
12627             cfg.cn = [
12628                 indicator,
12629                 {
12630                     tag: 'label',
12631                     'for' :  id,
12632                     cls : 'control-label col-form-label',
12633                     html : this.fieldLabel
12634
12635                 },
12636                 {
12637                     cls : "", 
12638                     cn: [
12639                         inputblock
12640                     ]
12641                 }
12642             ];
12643             
12644             var labelCfg = cfg.cn[1];
12645             var contentCfg = cfg.cn[2];
12646             
12647             if(this.indicatorpos == 'right'){
12648                 cfg.cn = [
12649                     {
12650                         tag: 'label',
12651                         'for' :  id,
12652                         cls : 'control-label col-form-label',
12653                         cn : [
12654                             {
12655                                 tag : 'span',
12656                                 html : this.fieldLabel
12657                             },
12658                             indicator
12659                         ]
12660                     },
12661                     {
12662                         cls : "",
12663                         cn: [
12664                             inputblock
12665                         ]
12666                     }
12667
12668                 ];
12669                 
12670                 labelCfg = cfg.cn[0];
12671                 contentCfg = cfg.cn[1];
12672             
12673             }
12674             
12675             if(this.labelWidth > 12){
12676                 labelCfg.style = "width: " + this.labelWidth + 'px';
12677             }
12678             
12679             if(this.labelWidth < 13 && this.labelmd == 0){
12680                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12681             }
12682             
12683             if(this.labellg > 0){
12684                 labelCfg.cls += ' col-lg-' + this.labellg;
12685                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12686             }
12687             
12688             if(this.labelmd > 0){
12689                 labelCfg.cls += ' col-md-' + this.labelmd;
12690                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12691             }
12692             
12693             if(this.labelsm > 0){
12694                 labelCfg.cls += ' col-sm-' + this.labelsm;
12695                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12696             }
12697             
12698             if(this.labelxs > 0){
12699                 labelCfg.cls += ' col-xs-' + this.labelxs;
12700                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12701             }
12702             
12703             
12704         } else if ( this.fieldLabel.length) {
12705                 
12706             
12707             
12708             cfg.cn = [
12709                 {
12710                     tag : 'i',
12711                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12712                     tooltip : 'This field is required',
12713                     style : this.allowBlank ? ' display:none' : '' 
12714                 },
12715                 {
12716                     tag: 'label',
12717                    //cls : 'input-group-addon',
12718                     html : this.fieldLabel
12719
12720                 },
12721
12722                inputblock
12723
12724            ];
12725            
12726            if(this.indicatorpos == 'right'){
12727        
12728                 cfg.cn = [
12729                     {
12730                         tag: 'label',
12731                        //cls : 'input-group-addon',
12732                         html : this.fieldLabel
12733
12734                     },
12735                     {
12736                         tag : 'i',
12737                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12738                         tooltip : 'This field is required',
12739                         style : this.allowBlank ? ' display:none' : '' 
12740                     },
12741
12742                    inputblock
12743
12744                ];
12745
12746             }
12747
12748         } else {
12749             
12750             cfg.cn = [
12751
12752                     inputblock
12753
12754             ];
12755                 
12756                 
12757         };
12758         
12759         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12760            cfg.cls += ' navbar-form';
12761         }
12762         
12763         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12764             // on BS4 we do this only if not form 
12765             cfg.cls += ' navbar-form';
12766             cfg.tag = 'li';
12767         }
12768         
12769         return cfg;
12770         
12771     },
12772     /**
12773      * return the real input element.
12774      */
12775     inputEl: function ()
12776     {
12777         return this.el.select('input.form-control',true).first();
12778     },
12779     
12780     tooltipEl : function()
12781     {
12782         return this.inputEl();
12783     },
12784     
12785     indicatorEl : function()
12786     {
12787         if (Roo.bootstrap.version == 4) {
12788             return false; // not enabled in v4 yet.
12789         }
12790         
12791         var indicator = this.el.select('i.roo-required-indicator',true).first();
12792         
12793         if(!indicator){
12794             return false;
12795         }
12796         
12797         return indicator;
12798         
12799     },
12800     
12801     setDisabled : function(v)
12802     {
12803         var i  = this.inputEl().dom;
12804         if (!v) {
12805             i.removeAttribute('disabled');
12806             return;
12807             
12808         }
12809         i.setAttribute('disabled','true');
12810     },
12811     initEvents : function()
12812     {
12813           
12814         this.inputEl().on("keydown" , this.fireKey,  this);
12815         this.inputEl().on("focus", this.onFocus,  this);
12816         this.inputEl().on("blur", this.onBlur,  this);
12817         
12818         this.inputEl().relayEvent('keyup', this);
12819         this.inputEl().relayEvent('paste', this);
12820         
12821         this.indicator = this.indicatorEl();
12822         
12823         if(this.indicator){
12824             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12825         }
12826  
12827         // reference to original value for reset
12828         this.originalValue = this.getValue();
12829         //Roo.form.TextField.superclass.initEvents.call(this);
12830         if(this.validationEvent == 'keyup'){
12831             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12832             this.inputEl().on('keyup', this.filterValidation, this);
12833         }
12834         else if(this.validationEvent !== false){
12835             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12836         }
12837         
12838         if(this.selectOnFocus){
12839             this.on("focus", this.preFocus, this);
12840             
12841         }
12842         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12843             this.inputEl().on("keypress", this.filterKeys, this);
12844         } else {
12845             this.inputEl().relayEvent('keypress', this);
12846         }
12847        /* if(this.grow){
12848             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12849             this.el.on("click", this.autoSize,  this);
12850         }
12851         */
12852         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12853             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12854         }
12855         
12856         if (typeof(this.before) == 'object') {
12857             this.before.render(this.el.select('.roo-input-before',true).first());
12858         }
12859         if (typeof(this.after) == 'object') {
12860             this.after.render(this.el.select('.roo-input-after',true).first());
12861         }
12862         
12863         this.inputEl().on('change', this.onChange, this);
12864         
12865     },
12866     filterValidation : function(e){
12867         if(!e.isNavKeyPress()){
12868             this.validationTask.delay(this.validationDelay);
12869         }
12870     },
12871      /**
12872      * Validates the field value
12873      * @return {Boolean} True if the value is valid, else false
12874      */
12875     validate : function(){
12876         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12877         if(this.disabled || this.validateValue(this.getRawValue())){
12878             this.markValid();
12879             return true;
12880         }
12881         
12882         this.markInvalid();
12883         return false;
12884     },
12885     
12886     
12887     /**
12888      * Validates a value according to the field's validation rules and marks the field as invalid
12889      * if the validation fails
12890      * @param {Mixed} value The value to validate
12891      * @return {Boolean} True if the value is valid, else false
12892      */
12893     validateValue : function(value)
12894     {
12895         if(this.getVisibilityEl().hasClass('hidden')){
12896             return true;
12897         }
12898         
12899         if(value.length < 1)  { // if it's blank
12900             if(this.allowBlank){
12901                 return true;
12902             }
12903             return false;
12904         }
12905         
12906         if(value.length < this.minLength){
12907             return false;
12908         }
12909         if(value.length > this.maxLength){
12910             return false;
12911         }
12912         if(this.vtype){
12913             var vt = Roo.form.VTypes;
12914             if(!vt[this.vtype](value, this)){
12915                 return false;
12916             }
12917         }
12918         if(typeof this.validator == "function"){
12919             var msg = this.validator(value);
12920             if(msg !== true){
12921                 return false;
12922             }
12923             if (typeof(msg) == 'string') {
12924                 this.invalidText = msg;
12925             }
12926         }
12927         
12928         if(this.regex && !this.regex.test(value)){
12929             return false;
12930         }
12931         
12932         return true;
12933     },
12934     
12935      // private
12936     fireKey : function(e){
12937         //Roo.log('field ' + e.getKey());
12938         if(e.isNavKeyPress()){
12939             this.fireEvent("specialkey", this, e);
12940         }
12941     },
12942     focus : function (selectText){
12943         if(this.rendered){
12944             this.inputEl().focus();
12945             if(selectText === true){
12946                 this.inputEl().dom.select();
12947             }
12948         }
12949         return this;
12950     } ,
12951     
12952     onFocus : function(){
12953         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12954            // this.el.addClass(this.focusClass);
12955         }
12956         if(!this.hasFocus){
12957             this.hasFocus = true;
12958             this.startValue = this.getValue();
12959             this.fireEvent("focus", this);
12960         }
12961     },
12962     
12963     beforeBlur : Roo.emptyFn,
12964
12965     
12966     // private
12967     onBlur : function(){
12968         this.beforeBlur();
12969         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12970             //this.el.removeClass(this.focusClass);
12971         }
12972         this.hasFocus = false;
12973         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12974             this.validate();
12975         }
12976         var v = this.getValue();
12977         if(String(v) !== String(this.startValue)){
12978             this.fireEvent('change', this, v, this.startValue);
12979         }
12980         this.fireEvent("blur", this);
12981     },
12982     
12983     onChange : function(e)
12984     {
12985         var v = this.getValue();
12986         if(String(v) !== String(this.startValue)){
12987             this.fireEvent('change', this, v, this.startValue);
12988         }
12989         
12990     },
12991     
12992     /**
12993      * Resets the current field value to the originally loaded value and clears any validation messages
12994      */
12995     reset : function(){
12996         this.setValue(this.originalValue);
12997         this.validate();
12998     },
12999      /**
13000      * Returns the name of the field
13001      * @return {Mixed} name The name field
13002      */
13003     getName: function(){
13004         return this.name;
13005     },
13006      /**
13007      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13008      * @return {Mixed} value The field value
13009      */
13010     getValue : function(){
13011         
13012         var v = this.inputEl().getValue();
13013         
13014         return v;
13015     },
13016     /**
13017      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13018      * @return {Mixed} value The field value
13019      */
13020     getRawValue : function(){
13021         var v = this.inputEl().getValue();
13022         
13023         return v;
13024     },
13025     
13026     /**
13027      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13028      * @param {Mixed} value The value to set
13029      */
13030     setRawValue : function(v){
13031         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13032     },
13033     
13034     selectText : function(start, end){
13035         var v = this.getRawValue();
13036         if(v.length > 0){
13037             start = start === undefined ? 0 : start;
13038             end = end === undefined ? v.length : end;
13039             var d = this.inputEl().dom;
13040             if(d.setSelectionRange){
13041                 d.setSelectionRange(start, end);
13042             }else if(d.createTextRange){
13043                 var range = d.createTextRange();
13044                 range.moveStart("character", start);
13045                 range.moveEnd("character", v.length-end);
13046                 range.select();
13047             }
13048         }
13049     },
13050     
13051     /**
13052      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13053      * @param {Mixed} value The value to set
13054      */
13055     setValue : function(v){
13056         this.value = v;
13057         if(this.rendered){
13058             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13059             this.validate();
13060         }
13061     },
13062     
13063     /*
13064     processValue : function(value){
13065         if(this.stripCharsRe){
13066             var newValue = value.replace(this.stripCharsRe, '');
13067             if(newValue !== value){
13068                 this.setRawValue(newValue);
13069                 return newValue;
13070             }
13071         }
13072         return value;
13073     },
13074   */
13075     preFocus : function(){
13076         
13077         if(this.selectOnFocus){
13078             this.inputEl().dom.select();
13079         }
13080     },
13081     filterKeys : function(e){
13082         var k = e.getKey();
13083         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13084             return;
13085         }
13086         var c = e.getCharCode(), cc = String.fromCharCode(c);
13087         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13088             return;
13089         }
13090         if(!this.maskRe.test(cc)){
13091             e.stopEvent();
13092         }
13093     },
13094      /**
13095      * Clear any invalid styles/messages for this field
13096      */
13097     clearInvalid : function(){
13098         
13099         if(!this.el || this.preventMark){ // not rendered
13100             return;
13101         }
13102         
13103         
13104         this.el.removeClass([this.invalidClass, 'is-invalid']);
13105         
13106         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13107             
13108             var feedback = this.el.select('.form-control-feedback', true).first();
13109             
13110             if(feedback){
13111                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13112             }
13113             
13114         }
13115         
13116         if(this.indicator){
13117             this.indicator.removeClass('visible');
13118             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13119         }
13120         
13121         this.fireEvent('valid', this);
13122     },
13123     
13124      /**
13125      * Mark this field as valid
13126      */
13127     markValid : function()
13128     {
13129         if(!this.el  || this.preventMark){ // not rendered...
13130             return;
13131         }
13132         
13133         this.el.removeClass([this.invalidClass, this.validClass]);
13134         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13135
13136         var feedback = this.el.select('.form-control-feedback', true).first();
13137             
13138         if(feedback){
13139             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13140         }
13141         
13142         if(this.indicator){
13143             this.indicator.removeClass('visible');
13144             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13145         }
13146         
13147         if(this.disabled){
13148             return;
13149         }
13150         
13151            
13152         if(this.allowBlank && !this.getRawValue().length){
13153             return;
13154         }
13155         if (Roo.bootstrap.version == 3) {
13156             this.el.addClass(this.validClass);
13157         } else {
13158             this.inputEl().addClass('is-valid');
13159         }
13160
13161         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13162             
13163             var feedback = this.el.select('.form-control-feedback', true).first();
13164             
13165             if(feedback){
13166                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13167                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13168             }
13169             
13170         }
13171         
13172         this.fireEvent('valid', this);
13173     },
13174     
13175      /**
13176      * Mark this field as invalid
13177      * @param {String} msg The validation message
13178      */
13179     markInvalid : function(msg)
13180     {
13181         if(!this.el  || this.preventMark){ // not rendered
13182             return;
13183         }
13184         
13185         this.el.removeClass([this.invalidClass, this.validClass]);
13186         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13187         
13188         var feedback = this.el.select('.form-control-feedback', true).first();
13189             
13190         if(feedback){
13191             this.el.select('.form-control-feedback', true).first().removeClass(
13192                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13193         }
13194
13195         if(this.disabled){
13196             return;
13197         }
13198         
13199         if(this.allowBlank && !this.getRawValue().length){
13200             return;
13201         }
13202         
13203         if(this.indicator){
13204             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13205             this.indicator.addClass('visible');
13206         }
13207         if (Roo.bootstrap.version == 3) {
13208             this.el.addClass(this.invalidClass);
13209         } else {
13210             this.inputEl().addClass('is-invalid');
13211         }
13212         
13213         
13214         
13215         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13216             
13217             var feedback = this.el.select('.form-control-feedback', true).first();
13218             
13219             if(feedback){
13220                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13221                 
13222                 if(this.getValue().length || this.forceFeedback){
13223                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13224                 }
13225                 
13226             }
13227             
13228         }
13229         
13230         this.fireEvent('invalid', this, msg);
13231     },
13232     // private
13233     SafariOnKeyDown : function(event)
13234     {
13235         // this is a workaround for a password hang bug on chrome/ webkit.
13236         if (this.inputEl().dom.type != 'password') {
13237             return;
13238         }
13239         
13240         var isSelectAll = false;
13241         
13242         if(this.inputEl().dom.selectionEnd > 0){
13243             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13244         }
13245         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13246             event.preventDefault();
13247             this.setValue('');
13248             return;
13249         }
13250         
13251         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13252             
13253             event.preventDefault();
13254             // this is very hacky as keydown always get's upper case.
13255             //
13256             var cc = String.fromCharCode(event.getCharCode());
13257             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13258             
13259         }
13260     },
13261     adjustWidth : function(tag, w){
13262         tag = tag.toLowerCase();
13263         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13264             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13265                 if(tag == 'input'){
13266                     return w + 2;
13267                 }
13268                 if(tag == 'textarea'){
13269                     return w-2;
13270                 }
13271             }else if(Roo.isOpera){
13272                 if(tag == 'input'){
13273                     return w + 2;
13274                 }
13275                 if(tag == 'textarea'){
13276                     return w-2;
13277                 }
13278             }
13279         }
13280         return w;
13281     },
13282     
13283     setFieldLabel : function(v)
13284     {
13285         if(!this.rendered){
13286             return;
13287         }
13288         
13289         if(this.indicatorEl()){
13290             var ar = this.el.select('label > span',true);
13291             
13292             if (ar.elements.length) {
13293                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13294                 this.fieldLabel = v;
13295                 return;
13296             }
13297             
13298             var br = this.el.select('label',true);
13299             
13300             if(br.elements.length) {
13301                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13302                 this.fieldLabel = v;
13303                 return;
13304             }
13305             
13306             Roo.log('Cannot Found any of label > span || label in input');
13307             return;
13308         }
13309         
13310         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13311         this.fieldLabel = v;
13312         
13313         
13314     }
13315 });
13316
13317  
13318 /*
13319  * - LGPL
13320  *
13321  * Input
13322  * 
13323  */
13324
13325 /**
13326  * @class Roo.bootstrap.form.TextArea
13327  * @extends Roo.bootstrap.form.Input
13328  * Bootstrap TextArea class
13329  * @cfg {Number} cols Specifies the visible width of a text area
13330  * @cfg {Number} rows Specifies the visible number of lines in a text area
13331  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13332  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13333  * @cfg {string} html text
13334  * 
13335  * @constructor
13336  * Create a new TextArea
13337  * @param {Object} config The config object
13338  */
13339
13340 Roo.bootstrap.form.TextArea = function(config){
13341     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13342    
13343 };
13344
13345 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13346      
13347     cols : false,
13348     rows : 5,
13349     readOnly : false,
13350     warp : 'soft',
13351     resize : false,
13352     value: false,
13353     html: false,
13354     
13355     getAutoCreate : function(){
13356         
13357         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13358         
13359         var id = Roo.id();
13360         
13361         var cfg = {};
13362         
13363         if(this.inputType != 'hidden'){
13364             cfg.cls = 'form-group' //input-group
13365         }
13366         
13367         var input =  {
13368             tag: 'textarea',
13369             id : id,
13370             warp : this.warp,
13371             rows : this.rows,
13372             value : this.value || '',
13373             html: this.html || '',
13374             cls : 'form-control',
13375             placeholder : this.placeholder || '' 
13376             
13377         };
13378         
13379         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13380             input.maxLength = this.maxLength;
13381         }
13382         
13383         if(this.resize){
13384             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13385         }
13386         
13387         if(this.cols){
13388             input.cols = this.cols;
13389         }
13390         
13391         if (this.readOnly) {
13392             input.readonly = true;
13393         }
13394         
13395         if (this.name) {
13396             input.name = this.name;
13397         }
13398         
13399         if (this.size) {
13400             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13401         }
13402         
13403         var settings=this;
13404         ['xs','sm','md','lg'].map(function(size){
13405             if (settings[size]) {
13406                 cfg.cls += ' col-' + size + '-' + settings[size];
13407             }
13408         });
13409         
13410         var inputblock = input;
13411         
13412         if(this.hasFeedback && !this.allowBlank){
13413             
13414             var feedback = {
13415                 tag: 'span',
13416                 cls: 'glyphicon form-control-feedback'
13417             };
13418
13419             inputblock = {
13420                 cls : 'has-feedback',
13421                 cn :  [
13422                     input,
13423                     feedback
13424                 ] 
13425             };  
13426         }
13427         
13428         
13429         if (this.before || this.after) {
13430             
13431             inputblock = {
13432                 cls : 'input-group',
13433                 cn :  [] 
13434             };
13435             if (this.before) {
13436                 inputblock.cn.push({
13437                     tag :'span',
13438                     cls : 'input-group-addon',
13439                     html : this.before
13440                 });
13441             }
13442             
13443             inputblock.cn.push(input);
13444             
13445             if(this.hasFeedback && !this.allowBlank){
13446                 inputblock.cls += ' has-feedback';
13447                 inputblock.cn.push(feedback);
13448             }
13449             
13450             if (this.after) {
13451                 inputblock.cn.push({
13452                     tag :'span',
13453                     cls : 'input-group-addon',
13454                     html : this.after
13455                 });
13456             }
13457             
13458         }
13459         
13460         if (align ==='left' && this.fieldLabel.length) {
13461             cfg.cn = [
13462                 {
13463                     tag: 'label',
13464                     'for' :  id,
13465                     cls : 'control-label',
13466                     html : this.fieldLabel
13467                 },
13468                 {
13469                     cls : "",
13470                     cn: [
13471                         inputblock
13472                     ]
13473                 }
13474
13475             ];
13476             
13477             if(this.labelWidth > 12){
13478                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13479             }
13480
13481             if(this.labelWidth < 13 && this.labelmd == 0){
13482                 this.labelmd = this.labelWidth;
13483             }
13484
13485             if(this.labellg > 0){
13486                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13487                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13488             }
13489
13490             if(this.labelmd > 0){
13491                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13492                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13493             }
13494
13495             if(this.labelsm > 0){
13496                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13497                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13498             }
13499
13500             if(this.labelxs > 0){
13501                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13502                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13503             }
13504             
13505         } else if ( this.fieldLabel.length) {
13506             cfg.cn = [
13507
13508                {
13509                    tag: 'label',
13510                    //cls : 'input-group-addon',
13511                    html : this.fieldLabel
13512
13513                },
13514
13515                inputblock
13516
13517            ];
13518
13519         } else {
13520
13521             cfg.cn = [
13522
13523                 inputblock
13524
13525             ];
13526                 
13527         }
13528         
13529         if (this.disabled) {
13530             input.disabled=true;
13531         }
13532         
13533         return cfg;
13534         
13535     },
13536     /**
13537      * return the real textarea element.
13538      */
13539     inputEl: function ()
13540     {
13541         return this.el.select('textarea.form-control',true).first();
13542     },
13543     
13544     /**
13545      * Clear any invalid styles/messages for this field
13546      */
13547     clearInvalid : function()
13548     {
13549         
13550         if(!this.el || this.preventMark){ // not rendered
13551             return;
13552         }
13553         
13554         var label = this.el.select('label', true).first();
13555         var icon = this.el.select('i.fa-star', true).first();
13556         
13557         if(label && icon){
13558             icon.remove();
13559         }
13560         this.el.removeClass( this.validClass);
13561         this.inputEl().removeClass('is-invalid');
13562          
13563         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13564             
13565             var feedback = this.el.select('.form-control-feedback', true).first();
13566             
13567             if(feedback){
13568                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13569             }
13570             
13571         }
13572         
13573         this.fireEvent('valid', this);
13574     },
13575     
13576      /**
13577      * Mark this field as valid
13578      */
13579     markValid : function()
13580     {
13581         if(!this.el  || this.preventMark){ // not rendered
13582             return;
13583         }
13584         
13585         this.el.removeClass([this.invalidClass, this.validClass]);
13586         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13587         
13588         var feedback = this.el.select('.form-control-feedback', true).first();
13589             
13590         if(feedback){
13591             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13592         }
13593
13594         if(this.disabled || this.allowBlank){
13595             return;
13596         }
13597         
13598         var label = this.el.select('label', true).first();
13599         var icon = this.el.select('i.fa-star', true).first();
13600         
13601         if(label && icon){
13602             icon.remove();
13603         }
13604         if (Roo.bootstrap.version == 3) {
13605             this.el.addClass(this.validClass);
13606         } else {
13607             this.inputEl().addClass('is-valid');
13608         }
13609         
13610         
13611         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13612             
13613             var feedback = this.el.select('.form-control-feedback', true).first();
13614             
13615             if(feedback){
13616                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13617                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13618             }
13619             
13620         }
13621         
13622         this.fireEvent('valid', this);
13623     },
13624     
13625      /**
13626      * Mark this field as invalid
13627      * @param {String} msg The validation message
13628      */
13629     markInvalid : function(msg)
13630     {
13631         if(!this.el  || this.preventMark){ // not rendered
13632             return;
13633         }
13634         
13635         this.el.removeClass([this.invalidClass, this.validClass]);
13636         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13637         
13638         var feedback = this.el.select('.form-control-feedback', true).first();
13639             
13640         if(feedback){
13641             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13642         }
13643
13644         if(this.disabled || this.allowBlank){
13645             return;
13646         }
13647         
13648         var label = this.el.select('label', true).first();
13649         var icon = this.el.select('i.fa-star', true).first();
13650         
13651         if(!this.getValue().length && label && !icon){
13652             this.el.createChild({
13653                 tag : 'i',
13654                 cls : 'text-danger fa fa-lg fa-star',
13655                 tooltip : 'This field is required',
13656                 style : 'margin-right:5px;'
13657             }, label, true);
13658         }
13659         
13660         if (Roo.bootstrap.version == 3) {
13661             this.el.addClass(this.invalidClass);
13662         } else {
13663             this.inputEl().addClass('is-invalid');
13664         }
13665         
13666         // fixme ... this may be depricated need to test..
13667         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13668             
13669             var feedback = this.el.select('.form-control-feedback', true).first();
13670             
13671             if(feedback){
13672                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13673                 
13674                 if(this.getValue().length || this.forceFeedback){
13675                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13676                 }
13677                 
13678             }
13679             
13680         }
13681         
13682         this.fireEvent('invalid', this, msg);
13683     }
13684 });
13685
13686  
13687 /*
13688  * - LGPL
13689  *
13690  * trigger field - base class for combo..
13691  * 
13692  */
13693  
13694 /**
13695  * @class Roo.bootstrap.form.TriggerField
13696  * @extends Roo.bootstrap.form.Input
13697  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13698  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13699  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13700  * for which you can provide a custom implementation.  For example:
13701  * <pre><code>
13702 var trigger = new Roo.bootstrap.form.TriggerField();
13703 trigger.onTriggerClick = myTriggerFn;
13704 trigger.applyTo('my-field');
13705 </code></pre>
13706  *
13707  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13708  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13709  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13710  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13711  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13712
13713  * @constructor
13714  * Create a new TriggerField.
13715  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13716  * to the base TextField)
13717  */
13718 Roo.bootstrap.form.TriggerField = function(config){
13719     this.mimicing = false;
13720     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13721 };
13722
13723 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13724     /**
13725      * @cfg {String} triggerClass A CSS class to apply to the trigger
13726      */
13727      /**
13728      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13729      */
13730     hideTrigger:false,
13731
13732     /**
13733      * @cfg {Boolean} removable (true|false) special filter default false
13734      */
13735     removable : false,
13736     
13737     /** @cfg {Boolean} grow @hide */
13738     /** @cfg {Number} growMin @hide */
13739     /** @cfg {Number} growMax @hide */
13740
13741     /**
13742      * @hide 
13743      * @method
13744      */
13745     autoSize: Roo.emptyFn,
13746     // private
13747     monitorTab : true,
13748     // private
13749     deferHeight : true,
13750
13751     
13752     actionMode : 'wrap',
13753     
13754     caret : false,
13755     
13756     
13757     getAutoCreate : function(){
13758        
13759         var align = this.labelAlign || this.parentLabelAlign();
13760         
13761         var id = Roo.id();
13762         
13763         var cfg = {
13764             cls: 'form-group' //input-group
13765         };
13766         
13767         
13768         var input =  {
13769             tag: 'input',
13770             id : id,
13771             type : this.inputType,
13772             cls : 'form-control',
13773             autocomplete: 'new-password',
13774             placeholder : this.placeholder || '' 
13775             
13776         };
13777         if (this.name) {
13778             input.name = this.name;
13779         }
13780         if (this.size) {
13781             input.cls += ' input-' + this.size;
13782         }
13783         
13784         if (this.disabled) {
13785             input.disabled=true;
13786         }
13787         
13788         var inputblock = input;
13789         
13790         if(this.hasFeedback && !this.allowBlank){
13791             
13792             var feedback = {
13793                 tag: 'span',
13794                 cls: 'glyphicon form-control-feedback'
13795             };
13796             
13797             if(this.removable && !this.editable  ){
13798                 inputblock = {
13799                     cls : 'has-feedback',
13800                     cn :  [
13801                         inputblock,
13802                         {
13803                             tag: 'button',
13804                             html : 'x',
13805                             cls : 'roo-combo-removable-btn close'
13806                         },
13807                         feedback
13808                     ] 
13809                 };
13810             } else {
13811                 inputblock = {
13812                     cls : 'has-feedback',
13813                     cn :  [
13814                         inputblock,
13815                         feedback
13816                     ] 
13817                 };
13818             }
13819
13820         } else {
13821             if(this.removable && !this.editable ){
13822                 inputblock = {
13823                     cls : 'roo-removable',
13824                     cn :  [
13825                         inputblock,
13826                         {
13827                             tag: 'button',
13828                             html : 'x',
13829                             cls : 'roo-combo-removable-btn close'
13830                         }
13831                     ] 
13832                 };
13833             }
13834         }
13835         
13836         if (this.before || this.after) {
13837             
13838             inputblock = {
13839                 cls : 'input-group',
13840                 cn :  [] 
13841             };
13842             if (this.before) {
13843                 inputblock.cn.push({
13844                     tag :'span',
13845                     cls : 'input-group-addon input-group-prepend input-group-text',
13846                     html : this.before
13847                 });
13848             }
13849             
13850             inputblock.cn.push(input);
13851             
13852             if(this.hasFeedback && !this.allowBlank){
13853                 inputblock.cls += ' has-feedback';
13854                 inputblock.cn.push(feedback);
13855             }
13856             
13857             if (this.after) {
13858                 inputblock.cn.push({
13859                     tag :'span',
13860                     cls : 'input-group-addon input-group-append input-group-text',
13861                     html : this.after
13862                 });
13863             }
13864             
13865         };
13866         
13867       
13868         
13869         var ibwrap = inputblock;
13870         
13871         if(this.multiple){
13872             ibwrap = {
13873                 tag: 'ul',
13874                 cls: 'roo-select2-choices',
13875                 cn:[
13876                     {
13877                         tag: 'li',
13878                         cls: 'roo-select2-search-field',
13879                         cn: [
13880
13881                             inputblock
13882                         ]
13883                     }
13884                 ]
13885             };
13886                 
13887         }
13888         
13889         var combobox = {
13890             cls: 'roo-select2-container input-group',
13891             cn: [
13892                  {
13893                     tag: 'input',
13894                     type : 'hidden',
13895                     cls: 'form-hidden-field'
13896                 },
13897                 ibwrap
13898             ]
13899         };
13900         
13901         if(!this.multiple && this.showToggleBtn){
13902             
13903             var caret = {
13904                         tag: 'span',
13905                         cls: 'caret'
13906              };
13907             if (this.caret != false) {
13908                 caret = {
13909                      tag: 'i',
13910                      cls: 'fa fa-' + this.caret
13911                 };
13912                 
13913             }
13914             
13915             combobox.cn.push({
13916                 tag :'span',
13917                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13918                 cn : [
13919                     Roo.bootstrap.version == 3 ? caret : '',
13920                     {
13921                         tag: 'span',
13922                         cls: 'combobox-clear',
13923                         cn  : [
13924                             {
13925                                 tag : 'i',
13926                                 cls: 'icon-remove'
13927                             }
13928                         ]
13929                     }
13930                 ]
13931
13932             })
13933         }
13934         
13935         if(this.multiple){
13936             combobox.cls += ' roo-select2-container-multi';
13937         }
13938          var indicator = {
13939             tag : 'i',
13940             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13941             tooltip : 'This field is required'
13942         };
13943         if (Roo.bootstrap.version == 4) {
13944             indicator = {
13945                 tag : 'i',
13946                 style : 'display:none'
13947             };
13948         }
13949         
13950         
13951         if (align ==='left' && this.fieldLabel.length) {
13952             
13953             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13954
13955             cfg.cn = [
13956                 indicator,
13957                 {
13958                     tag: 'label',
13959                     'for' :  id,
13960                     cls : 'control-label',
13961                     html : this.fieldLabel
13962
13963                 },
13964                 {
13965                     cls : "", 
13966                     cn: [
13967                         combobox
13968                     ]
13969                 }
13970
13971             ];
13972             
13973             var labelCfg = cfg.cn[1];
13974             var contentCfg = cfg.cn[2];
13975             
13976             if(this.indicatorpos == 'right'){
13977                 cfg.cn = [
13978                     {
13979                         tag: 'label',
13980                         'for' :  id,
13981                         cls : 'control-label',
13982                         cn : [
13983                             {
13984                                 tag : 'span',
13985                                 html : this.fieldLabel
13986                             },
13987                             indicator
13988                         ]
13989                     },
13990                     {
13991                         cls : "", 
13992                         cn: [
13993                             combobox
13994                         ]
13995                     }
13996
13997                 ];
13998                 
13999                 labelCfg = cfg.cn[0];
14000                 contentCfg = cfg.cn[1];
14001             }
14002             
14003             if(this.labelWidth > 12){
14004                 labelCfg.style = "width: " + this.labelWidth + 'px';
14005             }
14006             
14007             if(this.labelWidth < 13 && this.labelmd == 0){
14008                 this.labelmd = this.labelWidth;
14009             }
14010             
14011             if(this.labellg > 0){
14012                 labelCfg.cls += ' col-lg-' + this.labellg;
14013                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14014             }
14015             
14016             if(this.labelmd > 0){
14017                 labelCfg.cls += ' col-md-' + this.labelmd;
14018                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14019             }
14020             
14021             if(this.labelsm > 0){
14022                 labelCfg.cls += ' col-sm-' + this.labelsm;
14023                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14024             }
14025             
14026             if(this.labelxs > 0){
14027                 labelCfg.cls += ' col-xs-' + this.labelxs;
14028                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14029             }
14030             
14031         } else if ( this.fieldLabel.length) {
14032 //                Roo.log(" label");
14033             cfg.cn = [
14034                 indicator,
14035                {
14036                    tag: 'label',
14037                    //cls : 'input-group-addon',
14038                    html : this.fieldLabel
14039
14040                },
14041
14042                combobox
14043
14044             ];
14045             
14046             if(this.indicatorpos == 'right'){
14047                 
14048                 cfg.cn = [
14049                     {
14050                        tag: 'label',
14051                        cn : [
14052                            {
14053                                tag : 'span',
14054                                html : this.fieldLabel
14055                            },
14056                            indicator
14057                        ]
14058
14059                     },
14060                     combobox
14061
14062                 ];
14063
14064             }
14065
14066         } else {
14067             
14068 //                Roo.log(" no label && no align");
14069                 cfg = combobox
14070                      
14071                 
14072         }
14073         
14074         var settings=this;
14075         ['xs','sm','md','lg'].map(function(size){
14076             if (settings[size]) {
14077                 cfg.cls += ' col-' + size + '-' + settings[size];
14078             }
14079         });
14080         
14081         return cfg;
14082         
14083     },
14084     
14085     
14086     
14087     // private
14088     onResize : function(w, h){
14089 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14090 //        if(typeof w == 'number'){
14091 //            var x = w - this.trigger.getWidth();
14092 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14093 //            this.trigger.setStyle('left', x+'px');
14094 //        }
14095     },
14096
14097     // private
14098     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14099
14100     // private
14101     getResizeEl : function(){
14102         return this.inputEl();
14103     },
14104
14105     // private
14106     getPositionEl : function(){
14107         return this.inputEl();
14108     },
14109
14110     // private
14111     alignErrorIcon : function(){
14112         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14113     },
14114
14115     // private
14116     initEvents : function(){
14117         
14118         this.createList();
14119         
14120         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14121         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14122         if(!this.multiple && this.showToggleBtn){
14123             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14124             if(this.hideTrigger){
14125                 this.trigger.setDisplayed(false);
14126             }
14127             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14128         }
14129         
14130         if(this.multiple){
14131             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14132         }
14133         
14134         if(this.removable && !this.editable && !this.tickable){
14135             var close = this.closeTriggerEl();
14136             
14137             if(close){
14138                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14139                 close.on('click', this.removeBtnClick, this, close);
14140             }
14141         }
14142         
14143         //this.trigger.addClassOnOver('x-form-trigger-over');
14144         //this.trigger.addClassOnClick('x-form-trigger-click');
14145         
14146         //if(!this.width){
14147         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14148         //}
14149     },
14150     
14151     closeTriggerEl : function()
14152     {
14153         var close = this.el.select('.roo-combo-removable-btn', true).first();
14154         return close ? close : false;
14155     },
14156     
14157     removeBtnClick : function(e, h, el)
14158     {
14159         e.preventDefault();
14160         
14161         if(this.fireEvent("remove", this) !== false){
14162             this.reset();
14163             this.fireEvent("afterremove", this)
14164         }
14165     },
14166     
14167     createList : function()
14168     {
14169         this.list = Roo.get(document.body).createChild({
14170             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14171             cls: 'typeahead typeahead-long dropdown-menu shadow',
14172             style: 'display:none'
14173         });
14174         
14175         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14176         
14177     },
14178
14179     // private
14180     initTrigger : function(){
14181        
14182     },
14183
14184     // private
14185     onDestroy : function(){
14186         if(this.trigger){
14187             this.trigger.removeAllListeners();
14188           //  this.trigger.remove();
14189         }
14190         //if(this.wrap){
14191         //    this.wrap.remove();
14192         //}
14193         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14194     },
14195
14196     // private
14197     onFocus : function(){
14198         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14199         /*
14200         if(!this.mimicing){
14201             this.wrap.addClass('x-trigger-wrap-focus');
14202             this.mimicing = true;
14203             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14204             if(this.monitorTab){
14205                 this.el.on("keydown", this.checkTab, this);
14206             }
14207         }
14208         */
14209     },
14210
14211     // private
14212     checkTab : function(e){
14213         if(e.getKey() == e.TAB){
14214             this.triggerBlur();
14215         }
14216     },
14217
14218     // private
14219     onBlur : function(){
14220         // do nothing
14221     },
14222
14223     // private
14224     mimicBlur : function(e, t){
14225         /*
14226         if(!this.wrap.contains(t) && this.validateBlur()){
14227             this.triggerBlur();
14228         }
14229         */
14230     },
14231
14232     // private
14233     triggerBlur : function(){
14234         this.mimicing = false;
14235         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14236         if(this.monitorTab){
14237             this.el.un("keydown", this.checkTab, this);
14238         }
14239         //this.wrap.removeClass('x-trigger-wrap-focus');
14240         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14241     },
14242
14243     // private
14244     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14245     validateBlur : function(e, t){
14246         return true;
14247     },
14248
14249     // private
14250     onDisable : function(){
14251         this.inputEl().dom.disabled = true;
14252         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14253         //if(this.wrap){
14254         //    this.wrap.addClass('x-item-disabled');
14255         //}
14256     },
14257
14258     // private
14259     onEnable : function(){
14260         this.inputEl().dom.disabled = false;
14261         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14262         //if(this.wrap){
14263         //    this.el.removeClass('x-item-disabled');
14264         //}
14265     },
14266
14267     // private
14268     onShow : function(){
14269         var ae = this.getActionEl();
14270         
14271         if(ae){
14272             ae.dom.style.display = '';
14273             ae.dom.style.visibility = 'visible';
14274         }
14275     },
14276
14277     // private
14278     
14279     onHide : function(){
14280         var ae = this.getActionEl();
14281         ae.dom.style.display = 'none';
14282     },
14283
14284     /**
14285      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14286      * by an implementing function.
14287      * @method
14288      * @param {EventObject} e
14289      */
14290     onTriggerClick : Roo.emptyFn
14291 });
14292  
14293 /*
14294 * Licence: LGPL
14295 */
14296
14297 /**
14298  * @class Roo.bootstrap.form.CardUploader
14299  * @extends Roo.bootstrap.Button
14300  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14301  * @cfg {Number} errorTimeout default 3000
14302  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14303  * @cfg {Array}  html The button text.
14304
14305  *
14306  * @constructor
14307  * Create a new CardUploader
14308  * @param {Object} config The config object
14309  */
14310
14311 Roo.bootstrap.form.CardUploader = function(config){
14312     
14313  
14314     
14315     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14316     
14317     
14318     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14319         return r.data.id
14320      });
14321     
14322      this.addEvents({
14323          // raw events
14324         /**
14325          * @event preview
14326          * When a image is clicked on - and needs to display a slideshow or similar..
14327          * @param {Roo.bootstrap.Card} this
14328          * @param {Object} The image information data 
14329          *
14330          */
14331         'preview' : true,
14332          /**
14333          * @event download
14334          * When a the download link is clicked
14335          * @param {Roo.bootstrap.Card} this
14336          * @param {Object} The image information data  contains 
14337          */
14338         'download' : true
14339         
14340     });
14341 };
14342  
14343 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14344     
14345      
14346     errorTimeout : 3000,
14347      
14348     images : false,
14349    
14350     fileCollection : false,
14351     allowBlank : true,
14352     
14353     getAutoCreate : function()
14354     {
14355         
14356         var cfg =  {
14357             cls :'form-group' ,
14358             cn : [
14359                
14360                 {
14361                     tag: 'label',
14362                    //cls : 'input-group-addon',
14363                     html : this.fieldLabel
14364
14365                 },
14366
14367                 {
14368                     tag: 'input',
14369                     type : 'hidden',
14370                     name : this.name,
14371                     value : this.value,
14372                     cls : 'd-none  form-control'
14373                 },
14374                 
14375                 {
14376                     tag: 'input',
14377                     multiple : 'multiple',
14378                     type : 'file',
14379                     cls : 'd-none  roo-card-upload-selector'
14380                 },
14381                 
14382                 {
14383                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14384                 },
14385                 {
14386                     cls : 'card-columns roo-card-uploader-container'
14387                 }
14388
14389             ]
14390         };
14391            
14392          
14393         return cfg;
14394     },
14395     
14396     getChildContainer : function() /// what children are added to.
14397     {
14398         return this.containerEl;
14399     },
14400    
14401     getButtonContainer : function() /// what children are added to.
14402     {
14403         return this.el.select(".roo-card-uploader-button-container").first();
14404     },
14405    
14406     initEvents : function()
14407     {
14408         
14409         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14410         
14411         var t = this;
14412         this.addxtype({
14413             xns: Roo.bootstrap,
14414
14415             xtype : 'Button',
14416             container_method : 'getButtonContainer' ,            
14417             html :  this.html, // fix changable?
14418             cls : 'w-100 ',
14419             listeners : {
14420                 'click' : function(btn, e) {
14421                     t.onClick(e);
14422                 }
14423             }
14424         });
14425         
14426         
14427         
14428         
14429         this.urlAPI = (window.createObjectURL && window) || 
14430                                 (window.URL && URL.revokeObjectURL && URL) || 
14431                                 (window.webkitURL && webkitURL);
14432                         
14433          
14434          
14435          
14436         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14437         
14438         this.selectorEl.on('change', this.onFileSelected, this);
14439         if (this.images) {
14440             var t = this;
14441             this.images.forEach(function(img) {
14442                 t.addCard(img)
14443             });
14444             this.images = false;
14445         }
14446         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14447          
14448        
14449     },
14450     
14451    
14452     onClick : function(e)
14453     {
14454         e.preventDefault();
14455          
14456         this.selectorEl.dom.click();
14457          
14458     },
14459     
14460     onFileSelected : function(e)
14461     {
14462         e.preventDefault();
14463         
14464         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14465             return;
14466         }
14467         
14468         Roo.each(this.selectorEl.dom.files, function(file){    
14469             this.addFile(file);
14470         }, this);
14471          
14472     },
14473     
14474       
14475     
14476       
14477     
14478     addFile : function(file)
14479     {
14480            
14481         if(typeof(file) === 'string'){
14482             throw "Add file by name?"; // should not happen
14483             return;
14484         }
14485         
14486         if(!file || !this.urlAPI){
14487             return;
14488         }
14489         
14490         // file;
14491         // file.type;
14492         
14493         var _this = this;
14494         
14495         
14496         var url = _this.urlAPI.createObjectURL( file);
14497            
14498         this.addCard({
14499             id : Roo.bootstrap.form.CardUploader.ID--,
14500             is_uploaded : false,
14501             src : url,
14502             srcfile : file,
14503             title : file.name,
14504             mimetype : file.type,
14505             preview : false,
14506             is_deleted : 0
14507         });
14508         
14509     },
14510     
14511     /**
14512      * addCard - add an Attachment to the uploader
14513      * @param data - the data about the image to upload
14514      *
14515      * {
14516           id : 123
14517           title : "Title of file",
14518           is_uploaded : false,
14519           src : "http://.....",
14520           srcfile : { the File upload object },
14521           mimetype : file.type,
14522           preview : false,
14523           is_deleted : 0
14524           .. any other data...
14525         }
14526      *
14527      * 
14528     */
14529     
14530     addCard : function (data)
14531     {
14532         // hidden input element?
14533         // if the file is not an image...
14534         //then we need to use something other that and header_image
14535         var t = this;
14536         //   remove.....
14537         var footer = [
14538             {
14539                 xns : Roo.bootstrap,
14540                 xtype : 'CardFooter',
14541                  items: [
14542                     {
14543                         xns : Roo.bootstrap,
14544                         xtype : 'Element',
14545                         cls : 'd-flex',
14546                         items : [
14547                             
14548                             {
14549                                 xns : Roo.bootstrap,
14550                                 xtype : 'Button',
14551                                 html : String.format("<small>{0}</small>", data.title),
14552                                 cls : 'col-10 text-left',
14553                                 size: 'sm',
14554                                 weight: 'link',
14555                                 fa : 'download',
14556                                 listeners : {
14557                                     click : function() {
14558                                      
14559                                         t.fireEvent( "download", t, data );
14560                                     }
14561                                 }
14562                             },
14563                           
14564                             {
14565                                 xns : Roo.bootstrap,
14566                                 xtype : 'Button',
14567                                 style: 'max-height: 28px; ',
14568                                 size : 'sm',
14569                                 weight: 'danger',
14570                                 cls : 'col-2',
14571                                 fa : 'times',
14572                                 listeners : {
14573                                     click : function() {
14574                                         t.removeCard(data.id)
14575                                     }
14576                                 }
14577                             }
14578                         ]
14579                     }
14580                     
14581                 ] 
14582             }
14583             
14584         ];
14585         
14586         var cn = this.addxtype(
14587             {
14588                  
14589                 xns : Roo.bootstrap,
14590                 xtype : 'Card',
14591                 closeable : true,
14592                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14593                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14594                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14595                 data : data,
14596                 html : false,
14597                  
14598                 items : footer,
14599                 initEvents : function() {
14600                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14601                     var card = this;
14602                     this.imgEl = this.el.select('.card-img-top').first();
14603                     if (this.imgEl) {
14604                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14605                         this.imgEl.set({ 'pointer' : 'cursor' });
14606                                   
14607                     }
14608                     this.getCardFooter().addClass('p-1');
14609                     
14610                   
14611                 }
14612                 
14613             }
14614         );
14615         // dont' really need ot update items.
14616         // this.items.push(cn);
14617         this.fileCollection.add(cn);
14618         
14619         if (!data.srcfile) {
14620             this.updateInput();
14621             return;
14622         }
14623             
14624         var _t = this;
14625         var reader = new FileReader();
14626         reader.addEventListener("load", function() {  
14627             data.srcdata =  reader.result;
14628             _t.updateInput();
14629         });
14630         reader.readAsDataURL(data.srcfile);
14631         
14632         
14633         
14634     },
14635     removeCard : function(id)
14636     {
14637         
14638         var card  = this.fileCollection.get(id);
14639         card.data.is_deleted = 1;
14640         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14641         //this.fileCollection.remove(card);
14642         //this.items = this.items.filter(function(e) { return e != card });
14643         // dont' really need ot update items.
14644         card.el.dom.parentNode.removeChild(card.el.dom);
14645         this.updateInput();
14646
14647         
14648     },
14649     reset: function()
14650     {
14651         this.fileCollection.each(function(card) {
14652             if (card.el.dom && card.el.dom.parentNode) {
14653                 card.el.dom.parentNode.removeChild(card.el.dom);
14654             }
14655         });
14656         this.fileCollection.clear();
14657         this.updateInput();
14658     },
14659     
14660     updateInput : function()
14661     {
14662          var data = [];
14663         this.fileCollection.each(function(e) {
14664             data.push(e.data);
14665             
14666         });
14667         this.inputEl().dom.value = JSON.stringify(data);
14668         
14669         
14670         
14671     }
14672     
14673     
14674 });
14675
14676
14677 Roo.bootstrap.form.CardUploader.ID = -1;/*
14678  * Based on:
14679  * Ext JS Library 1.1.1
14680  * Copyright(c) 2006-2007, Ext JS, LLC.
14681  *
14682  * Originally Released Under LGPL - original licence link has changed is not relivant.
14683  *
14684  * Fork - LGPL
14685  * <script type="text/javascript">
14686  */
14687
14688
14689 /**
14690  * @class Roo.data.SortTypes
14691  * @static
14692  * Defines the default sorting (casting?) comparison functions used when sorting data.
14693  */
14694 Roo.data.SortTypes = {
14695     /**
14696      * Default sort that does nothing
14697      * @param {Mixed} s The value being converted
14698      * @return {Mixed} The comparison value
14699      */
14700     none : function(s){
14701         return s;
14702     },
14703     
14704     /**
14705      * The regular expression used to strip tags
14706      * @type {RegExp}
14707      * @property
14708      */
14709     stripTagsRE : /<\/?[^>]+>/gi,
14710     
14711     /**
14712      * Strips all HTML tags to sort on text only
14713      * @param {Mixed} s The value being converted
14714      * @return {String} The comparison value
14715      */
14716     asText : function(s){
14717         return String(s).replace(this.stripTagsRE, "");
14718     },
14719     
14720     /**
14721      * Strips all HTML tags to sort on text only - Case insensitive
14722      * @param {Mixed} s The value being converted
14723      * @return {String} The comparison value
14724      */
14725     asUCText : function(s){
14726         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14727     },
14728     
14729     /**
14730      * Case insensitive string
14731      * @param {Mixed} s The value being converted
14732      * @return {String} The comparison value
14733      */
14734     asUCString : function(s) {
14735         return String(s).toUpperCase();
14736     },
14737     
14738     /**
14739      * Date sorting
14740      * @param {Mixed} s The value being converted
14741      * @return {Number} The comparison value
14742      */
14743     asDate : function(s) {
14744         if(!s){
14745             return 0;
14746         }
14747         if(s instanceof Date){
14748             return s.getTime();
14749         }
14750         return Date.parse(String(s));
14751     },
14752     
14753     /**
14754      * Float sorting
14755      * @param {Mixed} s The value being converted
14756      * @return {Float} The comparison value
14757      */
14758     asFloat : function(s) {
14759         var val = parseFloat(String(s).replace(/,/g, ""));
14760         if(isNaN(val)) {
14761             val = 0;
14762         }
14763         return val;
14764     },
14765     
14766     /**
14767      * Integer sorting
14768      * @param {Mixed} s The value being converted
14769      * @return {Number} The comparison value
14770      */
14771     asInt : function(s) {
14772         var val = parseInt(String(s).replace(/,/g, ""));
14773         if(isNaN(val)) {
14774             val = 0;
14775         }
14776         return val;
14777     }
14778 };/*
14779  * Based on:
14780  * Ext JS Library 1.1.1
14781  * Copyright(c) 2006-2007, Ext JS, LLC.
14782  *
14783  * Originally Released Under LGPL - original licence link has changed is not relivant.
14784  *
14785  * Fork - LGPL
14786  * <script type="text/javascript">
14787  */
14788
14789 /**
14790 * @class Roo.data.Record
14791  * Instances of this class encapsulate both record <em>definition</em> information, and record
14792  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14793  * to access Records cached in an {@link Roo.data.Store} object.<br>
14794  * <p>
14795  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14796  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14797  * objects.<br>
14798  * <p>
14799  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14800  * @constructor
14801  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14802  * {@link #create}. The parameters are the same.
14803  * @param {Array} data An associative Array of data values keyed by the field name.
14804  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14805  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14806  * not specified an integer id is generated.
14807  */
14808 Roo.data.Record = function(data, id){
14809     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14810     this.data = data;
14811 };
14812
14813 /**
14814  * Generate a constructor for a specific record layout.
14815  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14816  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14817  * Each field definition object may contain the following properties: <ul>
14818  * <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,
14819  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14820  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14821  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14822  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14823  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14824  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14825  * this may be omitted.</p></li>
14826  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14827  * <ul><li>auto (Default, implies no conversion)</li>
14828  * <li>string</li>
14829  * <li>int</li>
14830  * <li>float</li>
14831  * <li>boolean</li>
14832  * <li>date</li></ul></p></li>
14833  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14834  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14835  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14836  * by the Reader into an object that will be stored in the Record. It is passed the
14837  * following parameters:<ul>
14838  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14839  * </ul></p></li>
14840  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14841  * </ul>
14842  * <br>usage:<br><pre><code>
14843 var TopicRecord = Roo.data.Record.create(
14844     {name: 'title', mapping: 'topic_title'},
14845     {name: 'author', mapping: 'username'},
14846     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14847     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14848     {name: 'lastPoster', mapping: 'user2'},
14849     {name: 'excerpt', mapping: 'post_text'}
14850 );
14851
14852 var myNewRecord = new TopicRecord({
14853     title: 'Do my job please',
14854     author: 'noobie',
14855     totalPosts: 1,
14856     lastPost: new Date(),
14857     lastPoster: 'Animal',
14858     excerpt: 'No way dude!'
14859 });
14860 myStore.add(myNewRecord);
14861 </code></pre>
14862  * @method create
14863  * @static
14864  */
14865 Roo.data.Record.create = function(o){
14866     var f = function(){
14867         f.superclass.constructor.apply(this, arguments);
14868     };
14869     Roo.extend(f, Roo.data.Record);
14870     var p = f.prototype;
14871     p.fields = new Roo.util.MixedCollection(false, function(field){
14872         return field.name;
14873     });
14874     for(var i = 0, len = o.length; i < len; i++){
14875         p.fields.add(new Roo.data.Field(o[i]));
14876     }
14877     f.getField = function(name){
14878         return p.fields.get(name);  
14879     };
14880     return f;
14881 };
14882
14883 Roo.data.Record.AUTO_ID = 1000;
14884 Roo.data.Record.EDIT = 'edit';
14885 Roo.data.Record.REJECT = 'reject';
14886 Roo.data.Record.COMMIT = 'commit';
14887
14888 Roo.data.Record.prototype = {
14889     /**
14890      * Readonly flag - true if this record has been modified.
14891      * @type Boolean
14892      */
14893     dirty : false,
14894     editing : false,
14895     error: null,
14896     modified: null,
14897
14898     // private
14899     join : function(store){
14900         this.store = store;
14901     },
14902
14903     /**
14904      * Set the named field to the specified value.
14905      * @param {String} name The name of the field to set.
14906      * @param {Object} value The value to set the field to.
14907      */
14908     set : function(name, value){
14909         if(this.data[name] == value){
14910             return;
14911         }
14912         this.dirty = true;
14913         if(!this.modified){
14914             this.modified = {};
14915         }
14916         if(typeof this.modified[name] == 'undefined'){
14917             this.modified[name] = this.data[name];
14918         }
14919         this.data[name] = value;
14920         if(!this.editing && this.store){
14921             this.store.afterEdit(this);
14922         }       
14923     },
14924
14925     /**
14926      * Get the value of the named field.
14927      * @param {String} name The name of the field to get the value of.
14928      * @return {Object} The value of the field.
14929      */
14930     get : function(name){
14931         return this.data[name]; 
14932     },
14933
14934     // private
14935     beginEdit : function(){
14936         this.editing = true;
14937         this.modified = {}; 
14938     },
14939
14940     // private
14941     cancelEdit : function(){
14942         this.editing = false;
14943         delete this.modified;
14944     },
14945
14946     // private
14947     endEdit : function(){
14948         this.editing = false;
14949         if(this.dirty && this.store){
14950             this.store.afterEdit(this);
14951         }
14952     },
14953
14954     /**
14955      * Usually called by the {@link Roo.data.Store} which owns the Record.
14956      * Rejects all changes made to the Record since either creation, or the last commit operation.
14957      * Modified fields are reverted to their original values.
14958      * <p>
14959      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14960      * of reject operations.
14961      */
14962     reject : function(){
14963         var m = this.modified;
14964         for(var n in m){
14965             if(typeof m[n] != "function"){
14966                 this.data[n] = m[n];
14967             }
14968         }
14969         this.dirty = false;
14970         delete this.modified;
14971         this.editing = false;
14972         if(this.store){
14973             this.store.afterReject(this);
14974         }
14975     },
14976
14977     /**
14978      * Usually called by the {@link Roo.data.Store} which owns the Record.
14979      * Commits all changes made to the Record since either creation, or the last commit operation.
14980      * <p>
14981      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14982      * of commit operations.
14983      */
14984     commit : function(){
14985         this.dirty = false;
14986         delete this.modified;
14987         this.editing = false;
14988         if(this.store){
14989             this.store.afterCommit(this);
14990         }
14991     },
14992
14993     // private
14994     hasError : function(){
14995         return this.error != null;
14996     },
14997
14998     // private
14999     clearError : function(){
15000         this.error = null;
15001     },
15002
15003     /**
15004      * Creates a copy of this record.
15005      * @param {String} id (optional) A new record id if you don't want to use this record's id
15006      * @return {Record}
15007      */
15008     copy : function(newId) {
15009         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15010     }
15011 };/*
15012  * Based on:
15013  * Ext JS Library 1.1.1
15014  * Copyright(c) 2006-2007, Ext JS, LLC.
15015  *
15016  * Originally Released Under LGPL - original licence link has changed is not relivant.
15017  *
15018  * Fork - LGPL
15019  * <script type="text/javascript">
15020  */
15021
15022
15023
15024 /**
15025  * @class Roo.data.Store
15026  * @extends Roo.util.Observable
15027  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15028  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15029  * <p>
15030  * 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
15031  * has no knowledge of the format of the data returned by the Proxy.<br>
15032  * <p>
15033  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15034  * instances from the data object. These records are cached and made available through accessor functions.
15035  * @constructor
15036  * Creates a new Store.
15037  * @param {Object} config A config object containing the objects needed for the Store to access data,
15038  * and read the data into Records.
15039  */
15040 Roo.data.Store = function(config){
15041     this.data = new Roo.util.MixedCollection(false);
15042     this.data.getKey = function(o){
15043         return o.id;
15044     };
15045     this.baseParams = {};
15046     // private
15047     this.paramNames = {
15048         "start" : "start",
15049         "limit" : "limit",
15050         "sort" : "sort",
15051         "dir" : "dir",
15052         "multisort" : "_multisort"
15053     };
15054
15055     if(config && config.data){
15056         this.inlineData = config.data;
15057         delete config.data;
15058     }
15059
15060     Roo.apply(this, config);
15061     
15062     if(this.reader){ // reader passed
15063         this.reader = Roo.factory(this.reader, Roo.data);
15064         this.reader.xmodule = this.xmodule || false;
15065         if(!this.recordType){
15066             this.recordType = this.reader.recordType;
15067         }
15068         if(this.reader.onMetaChange){
15069             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15070         }
15071     }
15072
15073     if(this.recordType){
15074         this.fields = this.recordType.prototype.fields;
15075     }
15076     this.modified = [];
15077
15078     this.addEvents({
15079         /**
15080          * @event datachanged
15081          * Fires when the data cache has changed, and a widget which is using this Store
15082          * as a Record cache should refresh its view.
15083          * @param {Store} this
15084          */
15085         datachanged : true,
15086         /**
15087          * @event metachange
15088          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15089          * @param {Store} this
15090          * @param {Object} meta The JSON metadata
15091          */
15092         metachange : true,
15093         /**
15094          * @event add
15095          * Fires when Records have been added to the Store
15096          * @param {Store} this
15097          * @param {Roo.data.Record[]} records The array of Records added
15098          * @param {Number} index The index at which the record(s) were added
15099          */
15100         add : true,
15101         /**
15102          * @event remove
15103          * Fires when a Record has been removed from the Store
15104          * @param {Store} this
15105          * @param {Roo.data.Record} record The Record that was removed
15106          * @param {Number} index The index at which the record was removed
15107          */
15108         remove : true,
15109         /**
15110          * @event update
15111          * Fires when a Record has been updated
15112          * @param {Store} this
15113          * @param {Roo.data.Record} record The Record that was updated
15114          * @param {String} operation The update operation being performed.  Value may be one of:
15115          * <pre><code>
15116  Roo.data.Record.EDIT
15117  Roo.data.Record.REJECT
15118  Roo.data.Record.COMMIT
15119          * </code></pre>
15120          */
15121         update : true,
15122         /**
15123          * @event clear
15124          * Fires when the data cache has been cleared.
15125          * @param {Store} this
15126          */
15127         clear : true,
15128         /**
15129          * @event beforeload
15130          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15131          * the load action will be canceled.
15132          * @param {Store} this
15133          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15134          */
15135         beforeload : true,
15136         /**
15137          * @event beforeloadadd
15138          * Fires after a new set of Records has been loaded.
15139          * @param {Store} this
15140          * @param {Roo.data.Record[]} records The Records that were loaded
15141          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15142          */
15143         beforeloadadd : true,
15144         /**
15145          * @event load
15146          * Fires after a new set of Records has been loaded, before they are added to the store.
15147          * @param {Store} this
15148          * @param {Roo.data.Record[]} records The Records that were loaded
15149          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15150          * @params {Object} return from reader
15151          */
15152         load : true,
15153         /**
15154          * @event loadexception
15155          * Fires if an exception occurs in the Proxy during loading.
15156          * Called with the signature of the Proxy's "loadexception" event.
15157          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15158          * 
15159          * @param {Proxy} 
15160          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15161          * @param {Object} load options 
15162          * @param {Object} jsonData from your request (normally this contains the Exception)
15163          */
15164         loadexception : true
15165     });
15166     
15167     if(this.proxy){
15168         this.proxy = Roo.factory(this.proxy, Roo.data);
15169         this.proxy.xmodule = this.xmodule || false;
15170         this.relayEvents(this.proxy,  ["loadexception"]);
15171     }
15172     this.sortToggle = {};
15173     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15174
15175     Roo.data.Store.superclass.constructor.call(this);
15176
15177     if(this.inlineData){
15178         this.loadData(this.inlineData);
15179         delete this.inlineData;
15180     }
15181 };
15182
15183 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15184      /**
15185     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15186     * without a remote query - used by combo/forms at present.
15187     */
15188     
15189     /**
15190     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15191     */
15192     /**
15193     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15194     */
15195     /**
15196     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15197     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15198     */
15199     /**
15200     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15201     * on any HTTP request
15202     */
15203     /**
15204     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15205     */
15206     /**
15207     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15208     */
15209     multiSort: false,
15210     /**
15211     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15212     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15213     */
15214     remoteSort : false,
15215
15216     /**
15217     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15218      * loaded or when a record is removed. (defaults to false).
15219     */
15220     pruneModifiedRecords : false,
15221
15222     // private
15223     lastOptions : null,
15224
15225     /**
15226      * Add Records to the Store and fires the add event.
15227      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15228      */
15229     add : function(records){
15230         records = [].concat(records);
15231         for(var i = 0, len = records.length; i < len; i++){
15232             records[i].join(this);
15233         }
15234         var index = this.data.length;
15235         this.data.addAll(records);
15236         this.fireEvent("add", this, records, index);
15237     },
15238
15239     /**
15240      * Remove a Record from the Store and fires the remove event.
15241      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15242      */
15243     remove : function(record){
15244         var index = this.data.indexOf(record);
15245         this.data.removeAt(index);
15246  
15247         if(this.pruneModifiedRecords){
15248             this.modified.remove(record);
15249         }
15250         this.fireEvent("remove", this, record, index);
15251     },
15252
15253     /**
15254      * Remove all Records from the Store and fires the clear event.
15255      */
15256     removeAll : function(){
15257         this.data.clear();
15258         if(this.pruneModifiedRecords){
15259             this.modified = [];
15260         }
15261         this.fireEvent("clear", this);
15262     },
15263
15264     /**
15265      * Inserts Records to the Store at the given index and fires the add event.
15266      * @param {Number} index The start index at which to insert the passed Records.
15267      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15268      */
15269     insert : function(index, records){
15270         records = [].concat(records);
15271         for(var i = 0, len = records.length; i < len; i++){
15272             this.data.insert(index, records[i]);
15273             records[i].join(this);
15274         }
15275         this.fireEvent("add", this, records, index);
15276     },
15277
15278     /**
15279      * Get the index within the cache of the passed Record.
15280      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15281      * @return {Number} The index of the passed Record. Returns -1 if not found.
15282      */
15283     indexOf : function(record){
15284         return this.data.indexOf(record);
15285     },
15286
15287     /**
15288      * Get the index within the cache of the Record with the passed id.
15289      * @param {String} id The id of the Record to find.
15290      * @return {Number} The index of the Record. Returns -1 if not found.
15291      */
15292     indexOfId : function(id){
15293         return this.data.indexOfKey(id);
15294     },
15295
15296     /**
15297      * Get the Record with the specified id.
15298      * @param {String} id The id of the Record to find.
15299      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15300      */
15301     getById : function(id){
15302         return this.data.key(id);
15303     },
15304
15305     /**
15306      * Get the Record at the specified index.
15307      * @param {Number} index The index of the Record to find.
15308      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15309      */
15310     getAt : function(index){
15311         return this.data.itemAt(index);
15312     },
15313
15314     /**
15315      * Returns a range of Records between specified indices.
15316      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15317      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15318      * @return {Roo.data.Record[]} An array of Records
15319      */
15320     getRange : function(start, end){
15321         return this.data.getRange(start, end);
15322     },
15323
15324     // private
15325     storeOptions : function(o){
15326         o = Roo.apply({}, o);
15327         delete o.callback;
15328         delete o.scope;
15329         this.lastOptions = o;
15330     },
15331
15332     /**
15333      * Loads the Record cache from the configured Proxy using the configured Reader.
15334      * <p>
15335      * If using remote paging, then the first load call must specify the <em>start</em>
15336      * and <em>limit</em> properties in the options.params property to establish the initial
15337      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15338      * <p>
15339      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15340      * and this call will return before the new data has been loaded. Perform any post-processing
15341      * in a callback function, or in a "load" event handler.</strong>
15342      * <p>
15343      * @param {Object} options An object containing properties which control loading options:<ul>
15344      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15345      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15346      * passed the following arguments:<ul>
15347      * <li>r : Roo.data.Record[]</li>
15348      * <li>options: Options object from the load call</li>
15349      * <li>success: Boolean success indicator</li></ul></li>
15350      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15351      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15352      * </ul>
15353      */
15354     load : function(options){
15355         options = options || {};
15356         if(this.fireEvent("beforeload", this, options) !== false){
15357             this.storeOptions(options);
15358             var p = Roo.apply(options.params || {}, this.baseParams);
15359             // if meta was not loaded from remote source.. try requesting it.
15360             if (!this.reader.metaFromRemote) {
15361                 p._requestMeta = 1;
15362             }
15363             if(this.sortInfo && this.remoteSort){
15364                 var pn = this.paramNames;
15365                 p[pn["sort"]] = this.sortInfo.field;
15366                 p[pn["dir"]] = this.sortInfo.direction;
15367             }
15368             if (this.multiSort) {
15369                 var pn = this.paramNames;
15370                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15371             }
15372             
15373             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15374         }
15375     },
15376
15377     /**
15378      * Reloads the Record cache from the configured Proxy using the configured Reader and
15379      * the options from the last load operation performed.
15380      * @param {Object} options (optional) An object containing properties which may override the options
15381      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15382      * the most recently used options are reused).
15383      */
15384     reload : function(options){
15385         this.load(Roo.applyIf(options||{}, this.lastOptions));
15386     },
15387
15388     // private
15389     // Called as a callback by the Reader during a load operation.
15390     loadRecords : function(o, options, success){
15391          
15392         if(!o){
15393             if(success !== false){
15394                 this.fireEvent("load", this, [], options, o);
15395             }
15396             if(options.callback){
15397                 options.callback.call(options.scope || this, [], options, false);
15398             }
15399             return;
15400         }
15401         // if data returned failure - throw an exception.
15402         if (o.success === false) {
15403             // show a message if no listener is registered.
15404             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15405                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15406             }
15407             // loadmask wil be hooked into this..
15408             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15409             return;
15410         }
15411         var r = o.records, t = o.totalRecords || r.length;
15412         
15413         this.fireEvent("beforeloadadd", this, r, options, o);
15414         
15415         if(!options || options.add !== true){
15416             if(this.pruneModifiedRecords){
15417                 this.modified = [];
15418             }
15419             for(var i = 0, len = r.length; i < len; i++){
15420                 r[i].join(this);
15421             }
15422             if(this.snapshot){
15423                 this.data = this.snapshot;
15424                 delete this.snapshot;
15425             }
15426             this.data.clear();
15427             this.data.addAll(r);
15428             this.totalLength = t;
15429             this.applySort();
15430             this.fireEvent("datachanged", this);
15431         }else{
15432             this.totalLength = Math.max(t, this.data.length+r.length);
15433             this.add(r);
15434         }
15435         
15436         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15437                 
15438             var e = new Roo.data.Record({});
15439
15440             e.set(this.parent.displayField, this.parent.emptyTitle);
15441             e.set(this.parent.valueField, '');
15442
15443             this.insert(0, e);
15444         }
15445             
15446         this.fireEvent("load", this, r, options, o);
15447         if(options.callback){
15448             options.callback.call(options.scope || this, r, options, true);
15449         }
15450     },
15451
15452
15453     /**
15454      * Loads data from a passed data block. A Reader which understands the format of the data
15455      * must have been configured in the constructor.
15456      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15457      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15458      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15459      */
15460     loadData : function(o, append){
15461         var r = this.reader.readRecords(o);
15462         this.loadRecords(r, {add: append}, true);
15463     },
15464     
15465      /**
15466      * using 'cn' the nested child reader read the child array into it's child stores.
15467      * @param {Object} rec The record with a 'children array
15468      */
15469     loadDataFromChildren : function(rec)
15470     {
15471         this.loadData(this.reader.toLoadData(rec));
15472     },
15473     
15474
15475     /**
15476      * Gets the number of cached records.
15477      * <p>
15478      * <em>If using paging, this may not be the total size of the dataset. If the data object
15479      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15480      * the data set size</em>
15481      */
15482     getCount : function(){
15483         return this.data.length || 0;
15484     },
15485
15486     /**
15487      * Gets the total number of records in the dataset as returned by the server.
15488      * <p>
15489      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15490      * the dataset size</em>
15491      */
15492     getTotalCount : function(){
15493         return this.totalLength || 0;
15494     },
15495
15496     /**
15497      * Returns the sort state of the Store as an object with two properties:
15498      * <pre><code>
15499  field {String} The name of the field by which the Records are sorted
15500  direction {String} The sort order, "ASC" or "DESC"
15501      * </code></pre>
15502      */
15503     getSortState : function(){
15504         return this.sortInfo;
15505     },
15506
15507     // private
15508     applySort : function(){
15509         if(this.sortInfo && !this.remoteSort){
15510             var s = this.sortInfo, f = s.field;
15511             var st = this.fields.get(f).sortType;
15512             var fn = function(r1, r2){
15513                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15514                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15515             };
15516             this.data.sort(s.direction, fn);
15517             if(this.snapshot && this.snapshot != this.data){
15518                 this.snapshot.sort(s.direction, fn);
15519             }
15520         }
15521     },
15522
15523     /**
15524      * Sets the default sort column and order to be used by the next load operation.
15525      * @param {String} fieldName The name of the field to sort by.
15526      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15527      */
15528     setDefaultSort : function(field, dir){
15529         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15530     },
15531
15532     /**
15533      * Sort the Records.
15534      * If remote sorting is used, the sort is performed on the server, and the cache is
15535      * reloaded. If local sorting is used, the cache is sorted internally.
15536      * @param {String} fieldName The name of the field to sort by.
15537      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15538      */
15539     sort : function(fieldName, dir){
15540         var f = this.fields.get(fieldName);
15541         if(!dir){
15542             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15543             
15544             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15545                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15546             }else{
15547                 dir = f.sortDir;
15548             }
15549         }
15550         this.sortToggle[f.name] = dir;
15551         this.sortInfo = {field: f.name, direction: dir};
15552         if(!this.remoteSort){
15553             this.applySort();
15554             this.fireEvent("datachanged", this);
15555         }else{
15556             this.load(this.lastOptions);
15557         }
15558     },
15559
15560     /**
15561      * Calls the specified function for each of the Records in the cache.
15562      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15563      * Returning <em>false</em> aborts and exits the iteration.
15564      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15565      */
15566     each : function(fn, scope){
15567         this.data.each(fn, scope);
15568     },
15569
15570     /**
15571      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15572      * (e.g., during paging).
15573      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15574      */
15575     getModifiedRecords : function(){
15576         return this.modified;
15577     },
15578
15579     // private
15580     createFilterFn : function(property, value, anyMatch){
15581         if(!value.exec){ // not a regex
15582             value = String(value);
15583             if(value.length == 0){
15584                 return false;
15585             }
15586             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15587         }
15588         return function(r){
15589             return value.test(r.data[property]);
15590         };
15591     },
15592
15593     /**
15594      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15595      * @param {String} property A field on your records
15596      * @param {Number} start The record index to start at (defaults to 0)
15597      * @param {Number} end The last record index to include (defaults to length - 1)
15598      * @return {Number} The sum
15599      */
15600     sum : function(property, start, end){
15601         var rs = this.data.items, v = 0;
15602         start = start || 0;
15603         end = (end || end === 0) ? end : rs.length-1;
15604
15605         for(var i = start; i <= end; i++){
15606             v += (rs[i].data[property] || 0);
15607         }
15608         return v;
15609     },
15610
15611     /**
15612      * Filter the records by a specified property.
15613      * @param {String} field A field on your records
15614      * @param {String/RegExp} value Either a string that the field
15615      * should start with or a RegExp to test against the field
15616      * @param {Boolean} anyMatch True to match any part not just the beginning
15617      */
15618     filter : function(property, value, anyMatch){
15619         var fn = this.createFilterFn(property, value, anyMatch);
15620         return fn ? this.filterBy(fn) : this.clearFilter();
15621     },
15622
15623     /**
15624      * Filter by a function. The specified function will be called with each
15625      * record in this data source. If the function returns true the record is included,
15626      * otherwise it is filtered.
15627      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15628      * @param {Object} scope (optional) The scope of the function (defaults to this)
15629      */
15630     filterBy : function(fn, scope){
15631         this.snapshot = this.snapshot || this.data;
15632         this.data = this.queryBy(fn, scope||this);
15633         this.fireEvent("datachanged", this);
15634     },
15635
15636     /**
15637      * Query the records by a specified property.
15638      * @param {String} field A field on your records
15639      * @param {String/RegExp} value Either a string that the field
15640      * should start with or a RegExp to test against the field
15641      * @param {Boolean} anyMatch True to match any part not just the beginning
15642      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15643      */
15644     query : function(property, value, anyMatch){
15645         var fn = this.createFilterFn(property, value, anyMatch);
15646         return fn ? this.queryBy(fn) : this.data.clone();
15647     },
15648
15649     /**
15650      * Query by a function. The specified function will be called with each
15651      * record in this data source. If the function returns true the record is included
15652      * in the results.
15653      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15654      * @param {Object} scope (optional) The scope of the function (defaults to this)
15655       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15656      **/
15657     queryBy : function(fn, scope){
15658         var data = this.snapshot || this.data;
15659         return data.filterBy(fn, scope||this);
15660     },
15661
15662     /**
15663      * Collects unique values for a particular dataIndex from this store.
15664      * @param {String} dataIndex The property to collect
15665      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15666      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15667      * @return {Array} An array of the unique values
15668      **/
15669     collect : function(dataIndex, allowNull, bypassFilter){
15670         var d = (bypassFilter === true && this.snapshot) ?
15671                 this.snapshot.items : this.data.items;
15672         var v, sv, r = [], l = {};
15673         for(var i = 0, len = d.length; i < len; i++){
15674             v = d[i].data[dataIndex];
15675             sv = String(v);
15676             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15677                 l[sv] = true;
15678                 r[r.length] = v;
15679             }
15680         }
15681         return r;
15682     },
15683
15684     /**
15685      * Revert to a view of the Record cache with no filtering applied.
15686      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15687      */
15688     clearFilter : function(suppressEvent){
15689         if(this.snapshot && this.snapshot != this.data){
15690             this.data = this.snapshot;
15691             delete this.snapshot;
15692             if(suppressEvent !== true){
15693                 this.fireEvent("datachanged", this);
15694             }
15695         }
15696     },
15697
15698     // private
15699     afterEdit : function(record){
15700         if(this.modified.indexOf(record) == -1){
15701             this.modified.push(record);
15702         }
15703         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15704     },
15705     
15706     // private
15707     afterReject : function(record){
15708         this.modified.remove(record);
15709         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15710     },
15711
15712     // private
15713     afterCommit : function(record){
15714         this.modified.remove(record);
15715         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15716     },
15717
15718     /**
15719      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15720      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15721      */
15722     commitChanges : function(){
15723         var m = this.modified.slice(0);
15724         this.modified = [];
15725         for(var i = 0, len = m.length; i < len; i++){
15726             m[i].commit();
15727         }
15728     },
15729
15730     /**
15731      * Cancel outstanding changes on all changed records.
15732      */
15733     rejectChanges : function(){
15734         var m = this.modified.slice(0);
15735         this.modified = [];
15736         for(var i = 0, len = m.length; i < len; i++){
15737             m[i].reject();
15738         }
15739     },
15740
15741     onMetaChange : function(meta, rtype, o){
15742         this.recordType = rtype;
15743         this.fields = rtype.prototype.fields;
15744         delete this.snapshot;
15745         this.sortInfo = meta.sortInfo || this.sortInfo;
15746         this.modified = [];
15747         this.fireEvent('metachange', this, this.reader.meta);
15748     },
15749     
15750     moveIndex : function(data, type)
15751     {
15752         var index = this.indexOf(data);
15753         
15754         var newIndex = index + type;
15755         
15756         this.remove(data);
15757         
15758         this.insert(newIndex, data);
15759         
15760     }
15761 });/*
15762  * Based on:
15763  * Ext JS Library 1.1.1
15764  * Copyright(c) 2006-2007, Ext JS, LLC.
15765  *
15766  * Originally Released Under LGPL - original licence link has changed is not relivant.
15767  *
15768  * Fork - LGPL
15769  * <script type="text/javascript">
15770  */
15771
15772 /**
15773  * @class Roo.data.SimpleStore
15774  * @extends Roo.data.Store
15775  * Small helper class to make creating Stores from Array data easier.
15776  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15777  * @cfg {Array} fields An array of field definition objects, or field name strings.
15778  * @cfg {Object} an existing reader (eg. copied from another store)
15779  * @cfg {Array} data The multi-dimensional array of data
15780  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15781  * @cfg {Roo.data.Reader} reader  [not-required] 
15782  * @constructor
15783  * @param {Object} config
15784  */
15785 Roo.data.SimpleStore = function(config)
15786 {
15787     Roo.data.SimpleStore.superclass.constructor.call(this, {
15788         isLocal : true,
15789         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15790                 id: config.id
15791             },
15792             Roo.data.Record.create(config.fields)
15793         ),
15794         proxy : new Roo.data.MemoryProxy(config.data)
15795     });
15796     this.load();
15797 };
15798 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15799  * Based on:
15800  * Ext JS Library 1.1.1
15801  * Copyright(c) 2006-2007, Ext JS, LLC.
15802  *
15803  * Originally Released Under LGPL - original licence link has changed is not relivant.
15804  *
15805  * Fork - LGPL
15806  * <script type="text/javascript">
15807  */
15808
15809 /**
15810 /**
15811  * @extends Roo.data.Store
15812  * @class Roo.data.JsonStore
15813  * Small helper class to make creating Stores for JSON data easier. <br/>
15814 <pre><code>
15815 var store = new Roo.data.JsonStore({
15816     url: 'get-images.php',
15817     root: 'images',
15818     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15819 });
15820 </code></pre>
15821  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15822  * JsonReader and HttpProxy (unless inline data is provided).</b>
15823  * @cfg {Array} fields An array of field definition objects, or field name strings.
15824  * @constructor
15825  * @param {Object} config
15826  */
15827 Roo.data.JsonStore = function(c){
15828     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15829         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15830         reader: new Roo.data.JsonReader(c, c.fields)
15831     }));
15832 };
15833 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15834  * Based on:
15835  * Ext JS Library 1.1.1
15836  * Copyright(c) 2006-2007, Ext JS, LLC.
15837  *
15838  * Originally Released Under LGPL - original licence link has changed is not relivant.
15839  *
15840  * Fork - LGPL
15841  * <script type="text/javascript">
15842  */
15843
15844  
15845 Roo.data.Field = function(config){
15846     if(typeof config == "string"){
15847         config = {name: config};
15848     }
15849     Roo.apply(this, config);
15850     
15851     if(!this.type){
15852         this.type = "auto";
15853     }
15854     
15855     var st = Roo.data.SortTypes;
15856     // named sortTypes are supported, here we look them up
15857     if(typeof this.sortType == "string"){
15858         this.sortType = st[this.sortType];
15859     }
15860     
15861     // set default sortType for strings and dates
15862     if(!this.sortType){
15863         switch(this.type){
15864             case "string":
15865                 this.sortType = st.asUCString;
15866                 break;
15867             case "date":
15868                 this.sortType = st.asDate;
15869                 break;
15870             default:
15871                 this.sortType = st.none;
15872         }
15873     }
15874
15875     // define once
15876     var stripRe = /[\$,%]/g;
15877
15878     // prebuilt conversion function for this field, instead of
15879     // switching every time we're reading a value
15880     if(!this.convert){
15881         var cv, dateFormat = this.dateFormat;
15882         switch(this.type){
15883             case "":
15884             case "auto":
15885             case undefined:
15886                 cv = function(v){ return v; };
15887                 break;
15888             case "string":
15889                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15890                 break;
15891             case "int":
15892                 cv = function(v){
15893                     return v !== undefined && v !== null && v !== '' ?
15894                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15895                     };
15896                 break;
15897             case "float":
15898                 cv = function(v){
15899                     return v !== undefined && v !== null && v !== '' ?
15900                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15901                     };
15902                 break;
15903             case "bool":
15904             case "boolean":
15905                 cv = function(v){ return v === true || v === "true" || v == 1; };
15906                 break;
15907             case "date":
15908                 cv = function(v){
15909                     if(!v){
15910                         return '';
15911                     }
15912                     if(v instanceof Date){
15913                         return v;
15914                     }
15915                     if(dateFormat){
15916                         if(dateFormat == "timestamp"){
15917                             return new Date(v*1000);
15918                         }
15919                         return Date.parseDate(v, dateFormat);
15920                     }
15921                     var parsed = Date.parse(v);
15922                     return parsed ? new Date(parsed) : null;
15923                 };
15924              break;
15925             
15926         }
15927         this.convert = cv;
15928     }
15929 };
15930
15931 Roo.data.Field.prototype = {
15932     dateFormat: null,
15933     defaultValue: "",
15934     mapping: null,
15935     sortType : null,
15936     sortDir : "ASC"
15937 };/*
15938  * Based on:
15939  * Ext JS Library 1.1.1
15940  * Copyright(c) 2006-2007, Ext JS, LLC.
15941  *
15942  * Originally Released Under LGPL - original licence link has changed is not relivant.
15943  *
15944  * Fork - LGPL
15945  * <script type="text/javascript">
15946  */
15947  
15948 // Base class for reading structured data from a data source.  This class is intended to be
15949 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15950
15951 /**
15952  * @class Roo.data.DataReader
15953  * @abstract
15954  * Base class for reading structured data from a data source.  This class is intended to be
15955  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15956  */
15957
15958 Roo.data.DataReader = function(meta, recordType){
15959     
15960     this.meta = meta;
15961     
15962     this.recordType = recordType instanceof Array ? 
15963         Roo.data.Record.create(recordType) : recordType;
15964 };
15965
15966 Roo.data.DataReader.prototype = {
15967     
15968     
15969     readerType : 'Data',
15970      /**
15971      * Create an empty record
15972      * @param {Object} data (optional) - overlay some values
15973      * @return {Roo.data.Record} record created.
15974      */
15975     newRow :  function(d) {
15976         var da =  {};
15977         this.recordType.prototype.fields.each(function(c) {
15978             switch( c.type) {
15979                 case 'int' : da[c.name] = 0; break;
15980                 case 'date' : da[c.name] = new Date(); break;
15981                 case 'float' : da[c.name] = 0.0; break;
15982                 case 'boolean' : da[c.name] = false; break;
15983                 default : da[c.name] = ""; break;
15984             }
15985             
15986         });
15987         return new this.recordType(Roo.apply(da, d));
15988     }
15989     
15990     
15991 };/*
15992  * Based on:
15993  * Ext JS Library 1.1.1
15994  * Copyright(c) 2006-2007, Ext JS, LLC.
15995  *
15996  * Originally Released Under LGPL - original licence link has changed is not relivant.
15997  *
15998  * Fork - LGPL
15999  * <script type="text/javascript">
16000  */
16001
16002 /**
16003  * @class Roo.data.DataProxy
16004  * @extends Roo.util.Observable
16005  * @abstract
16006  * This class is an abstract base class for implementations which provide retrieval of
16007  * unformatted data objects.<br>
16008  * <p>
16009  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16010  * (of the appropriate type which knows how to parse the data object) to provide a block of
16011  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16012  * <p>
16013  * Custom implementations must implement the load method as described in
16014  * {@link Roo.data.HttpProxy#load}.
16015  */
16016 Roo.data.DataProxy = function(){
16017     this.addEvents({
16018         /**
16019          * @event beforeload
16020          * Fires before a network request is made to retrieve a data object.
16021          * @param {Object} This DataProxy object.
16022          * @param {Object} params The params parameter to the load function.
16023          */
16024         beforeload : true,
16025         /**
16026          * @event load
16027          * Fires before the load method's callback is called.
16028          * @param {Object} This DataProxy object.
16029          * @param {Object} o The data object.
16030          * @param {Object} arg The callback argument object passed to the load function.
16031          */
16032         load : true,
16033         /**
16034          * @event loadexception
16035          * Fires if an Exception occurs during data retrieval.
16036          * @param {Object} This DataProxy object.
16037          * @param {Object} o The data object.
16038          * @param {Object} arg The callback argument object passed to the load function.
16039          * @param {Object} e The Exception.
16040          */
16041         loadexception : true
16042     });
16043     Roo.data.DataProxy.superclass.constructor.call(this);
16044 };
16045
16046 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16047
16048     /**
16049      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16050      */
16051 /*
16052  * Based on:
16053  * Ext JS Library 1.1.1
16054  * Copyright(c) 2006-2007, Ext JS, LLC.
16055  *
16056  * Originally Released Under LGPL - original licence link has changed is not relivant.
16057  *
16058  * Fork - LGPL
16059  * <script type="text/javascript">
16060  */
16061 /**
16062  * @class Roo.data.MemoryProxy
16063  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16064  * to the Reader when its load method is called.
16065  * @constructor
16066  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16067  */
16068 Roo.data.MemoryProxy = function(data){
16069     if (data.data) {
16070         data = data.data;
16071     }
16072     Roo.data.MemoryProxy.superclass.constructor.call(this);
16073     this.data = data;
16074 };
16075
16076 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16077     
16078     /**
16079      * Load data from the requested source (in this case an in-memory
16080      * data object passed to the constructor), read the data object into
16081      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16082      * process that block using the passed callback.
16083      * @param {Object} params This parameter is not used by the MemoryProxy class.
16084      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16085      * object into a block of Roo.data.Records.
16086      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16087      * The function must be passed <ul>
16088      * <li>The Record block object</li>
16089      * <li>The "arg" argument from the load function</li>
16090      * <li>A boolean success indicator</li>
16091      * </ul>
16092      * @param {Object} scope The scope in which to call the callback
16093      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16094      */
16095     load : function(params, reader, callback, scope, arg){
16096         params = params || {};
16097         var result;
16098         try {
16099             result = reader.readRecords(params.data ? params.data :this.data);
16100         }catch(e){
16101             this.fireEvent("loadexception", this, arg, null, e);
16102             callback.call(scope, null, arg, false);
16103             return;
16104         }
16105         callback.call(scope, result, arg, true);
16106     },
16107     
16108     // private
16109     update : function(params, records){
16110         
16111     }
16112 });/*
16113  * Based on:
16114  * Ext JS Library 1.1.1
16115  * Copyright(c) 2006-2007, Ext JS, LLC.
16116  *
16117  * Originally Released Under LGPL - original licence link has changed is not relivant.
16118  *
16119  * Fork - LGPL
16120  * <script type="text/javascript">
16121  */
16122 /**
16123  * @class Roo.data.HttpProxy
16124  * @extends Roo.data.DataProxy
16125  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16126  * configured to reference a certain URL.<br><br>
16127  * <p>
16128  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16129  * from which the running page was served.<br><br>
16130  * <p>
16131  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16132  * <p>
16133  * Be aware that to enable the browser to parse an XML document, the server must set
16134  * the Content-Type header in the HTTP response to "text/xml".
16135  * @constructor
16136  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16137  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16138  * will be used to make the request.
16139  */
16140 Roo.data.HttpProxy = function(conn){
16141     Roo.data.HttpProxy.superclass.constructor.call(this);
16142     // is conn a conn config or a real conn?
16143     this.conn = conn;
16144     this.useAjax = !conn || !conn.events;
16145   
16146 };
16147
16148 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16149     // thse are take from connection...
16150     
16151     /**
16152      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16153      */
16154     /**
16155      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16156      * extra parameters to each request made by this object. (defaults to undefined)
16157      */
16158     /**
16159      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16160      *  to each request made by this object. (defaults to undefined)
16161      */
16162     /**
16163      * @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)
16164      */
16165     /**
16166      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16167      */
16168      /**
16169      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16170      * @type Boolean
16171      */
16172   
16173
16174     /**
16175      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16176      * @type Boolean
16177      */
16178     /**
16179      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16180      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16181      * a finer-grained basis than the DataProxy events.
16182      */
16183     getConnection : function(){
16184         return this.useAjax ? Roo.Ajax : this.conn;
16185     },
16186
16187     /**
16188      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16189      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16190      * process that block using the passed callback.
16191      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16192      * for the request to the remote server.
16193      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16194      * object into a block of Roo.data.Records.
16195      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16196      * The function must be passed <ul>
16197      * <li>The Record block object</li>
16198      * <li>The "arg" argument from the load function</li>
16199      * <li>A boolean success indicator</li>
16200      * </ul>
16201      * @param {Object} scope The scope in which to call the callback
16202      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16203      */
16204     load : function(params, reader, callback, scope, arg){
16205         if(this.fireEvent("beforeload", this, params) !== false){
16206             var  o = {
16207                 params : params || {},
16208                 request: {
16209                     callback : callback,
16210                     scope : scope,
16211                     arg : arg
16212                 },
16213                 reader: reader,
16214                 callback : this.loadResponse,
16215                 scope: this
16216             };
16217             if(this.useAjax){
16218                 Roo.applyIf(o, this.conn);
16219                 if(this.activeRequest){
16220                     Roo.Ajax.abort(this.activeRequest);
16221                 }
16222                 this.activeRequest = Roo.Ajax.request(o);
16223             }else{
16224                 this.conn.request(o);
16225             }
16226         }else{
16227             callback.call(scope||this, null, arg, false);
16228         }
16229     },
16230
16231     // private
16232     loadResponse : function(o, success, response){
16233         delete this.activeRequest;
16234         if(!success){
16235             this.fireEvent("loadexception", this, o, response);
16236             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16237             return;
16238         }
16239         var result;
16240         try {
16241             result = o.reader.read(response);
16242         }catch(e){
16243             this.fireEvent("loadexception", this, o, response, e);
16244             o.request.callback.call(o.request.scope, {
16245                     success : false,
16246                     raw : {
16247                         errorMsg : response.responseText
16248                     }
16249                     
16250                 }, o.request.arg, false);
16251             return;
16252         }
16253         
16254         this.fireEvent("load", this, o, o.request.arg);
16255         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16256     },
16257
16258     // private
16259     update : function(dataSet){
16260
16261     },
16262
16263     // private
16264     updateResponse : function(dataSet){
16265
16266     }
16267 });/*
16268  * Based on:
16269  * Ext JS Library 1.1.1
16270  * Copyright(c) 2006-2007, Ext JS, LLC.
16271  *
16272  * Originally Released Under LGPL - original licence link has changed is not relivant.
16273  *
16274  * Fork - LGPL
16275  * <script type="text/javascript">
16276  */
16277
16278 /**
16279  * @class Roo.data.ScriptTagProxy
16280  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16281  * other than the originating domain of the running page.<br><br>
16282  * <p>
16283  * <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
16284  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16285  * <p>
16286  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16287  * source code that is used as the source inside a &lt;script> tag.<br><br>
16288  * <p>
16289  * In order for the browser to process the returned data, the server must wrap the data object
16290  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16291  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16292  * depending on whether the callback name was passed:
16293  * <p>
16294  * <pre><code>
16295 boolean scriptTag = false;
16296 String cb = request.getParameter("callback");
16297 if (cb != null) {
16298     scriptTag = true;
16299     response.setContentType("text/javascript");
16300 } else {
16301     response.setContentType("application/x-json");
16302 }
16303 Writer out = response.getWriter();
16304 if (scriptTag) {
16305     out.write(cb + "(");
16306 }
16307 out.print(dataBlock.toJsonString());
16308 if (scriptTag) {
16309     out.write(");");
16310 }
16311 </pre></code>
16312  *
16313  * @constructor
16314  * @param {Object} config A configuration object.
16315  */
16316 Roo.data.ScriptTagProxy = function(config){
16317     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16318     Roo.apply(this, config);
16319     this.head = document.getElementsByTagName("head")[0];
16320 };
16321
16322 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16323
16324 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16325     /**
16326      * @cfg {String} url The URL from which to request the data object.
16327      */
16328     /**
16329      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16330      */
16331     timeout : 30000,
16332     /**
16333      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16334      * the server the name of the callback function set up by the load call to process the returned data object.
16335      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16336      * javascript output which calls this named function passing the data object as its only parameter.
16337      */
16338     callbackParam : "callback",
16339     /**
16340      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16341      * name to the request.
16342      */
16343     nocache : true,
16344
16345     /**
16346      * Load data from the configured URL, read the data object into
16347      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16348      * process that block using the passed callback.
16349      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16350      * for the request to the remote server.
16351      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16352      * object into a block of Roo.data.Records.
16353      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16354      * The function must be passed <ul>
16355      * <li>The Record block object</li>
16356      * <li>The "arg" argument from the load function</li>
16357      * <li>A boolean success indicator</li>
16358      * </ul>
16359      * @param {Object} scope The scope in which to call the callback
16360      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16361      */
16362     load : function(params, reader, callback, scope, arg){
16363         if(this.fireEvent("beforeload", this, params) !== false){
16364
16365             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16366
16367             var url = this.url;
16368             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16369             if(this.nocache){
16370                 url += "&_dc=" + (new Date().getTime());
16371             }
16372             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16373             var trans = {
16374                 id : transId,
16375                 cb : "stcCallback"+transId,
16376                 scriptId : "stcScript"+transId,
16377                 params : params,
16378                 arg : arg,
16379                 url : url,
16380                 callback : callback,
16381                 scope : scope,
16382                 reader : reader
16383             };
16384             var conn = this;
16385
16386             window[trans.cb] = function(o){
16387                 conn.handleResponse(o, trans);
16388             };
16389
16390             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16391
16392             if(this.autoAbort !== false){
16393                 this.abort();
16394             }
16395
16396             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16397
16398             var script = document.createElement("script");
16399             script.setAttribute("src", url);
16400             script.setAttribute("type", "text/javascript");
16401             script.setAttribute("id", trans.scriptId);
16402             this.head.appendChild(script);
16403
16404             this.trans = trans;
16405         }else{
16406             callback.call(scope||this, null, arg, false);
16407         }
16408     },
16409
16410     // private
16411     isLoading : function(){
16412         return this.trans ? true : false;
16413     },
16414
16415     /**
16416      * Abort the current server request.
16417      */
16418     abort : function(){
16419         if(this.isLoading()){
16420             this.destroyTrans(this.trans);
16421         }
16422     },
16423
16424     // private
16425     destroyTrans : function(trans, isLoaded){
16426         this.head.removeChild(document.getElementById(trans.scriptId));
16427         clearTimeout(trans.timeoutId);
16428         if(isLoaded){
16429             window[trans.cb] = undefined;
16430             try{
16431                 delete window[trans.cb];
16432             }catch(e){}
16433         }else{
16434             // if hasn't been loaded, wait for load to remove it to prevent script error
16435             window[trans.cb] = function(){
16436                 window[trans.cb] = undefined;
16437                 try{
16438                     delete window[trans.cb];
16439                 }catch(e){}
16440             };
16441         }
16442     },
16443
16444     // private
16445     handleResponse : function(o, trans){
16446         this.trans = false;
16447         this.destroyTrans(trans, true);
16448         var result;
16449         try {
16450             result = trans.reader.readRecords(o);
16451         }catch(e){
16452             this.fireEvent("loadexception", this, o, trans.arg, e);
16453             trans.callback.call(trans.scope||window, null, trans.arg, false);
16454             return;
16455         }
16456         this.fireEvent("load", this, o, trans.arg);
16457         trans.callback.call(trans.scope||window, result, trans.arg, true);
16458     },
16459
16460     // private
16461     handleFailure : function(trans){
16462         this.trans = false;
16463         this.destroyTrans(trans, false);
16464         this.fireEvent("loadexception", this, null, trans.arg);
16465         trans.callback.call(trans.scope||window, null, trans.arg, false);
16466     }
16467 });/*
16468  * Based on:
16469  * Ext JS Library 1.1.1
16470  * Copyright(c) 2006-2007, Ext JS, LLC.
16471  *
16472  * Originally Released Under LGPL - original licence link has changed is not relivant.
16473  *
16474  * Fork - LGPL
16475  * <script type="text/javascript">
16476  */
16477
16478 /**
16479  * @class Roo.data.JsonReader
16480  * @extends Roo.data.DataReader
16481  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16482  * based on mappings in a provided Roo.data.Record constructor.
16483  * 
16484  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16485  * in the reply previously. 
16486  * 
16487  * <p>
16488  * Example code:
16489  * <pre><code>
16490 var RecordDef = Roo.data.Record.create([
16491     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16492     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16493 ]);
16494 var myReader = new Roo.data.JsonReader({
16495     totalProperty: "results",    // The property which contains the total dataset size (optional)
16496     root: "rows",                // The property which contains an Array of row objects
16497     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16498 }, RecordDef);
16499 </code></pre>
16500  * <p>
16501  * This would consume a JSON file like this:
16502  * <pre><code>
16503 { 'results': 2, 'rows': [
16504     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16505     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16506 }
16507 </code></pre>
16508  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16509  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16510  * paged from the remote server.
16511  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16512  * @cfg {String} root name of the property which contains the Array of row objects.
16513  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16514  * @cfg {Array} fields Array of field definition objects
16515  * @constructor
16516  * Create a new JsonReader
16517  * @param {Object} meta Metadata configuration options
16518  * @param {Object} recordType Either an Array of field definition objects,
16519  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16520  */
16521 Roo.data.JsonReader = function(meta, recordType){
16522     
16523     meta = meta || {};
16524     // set some defaults:
16525     Roo.applyIf(meta, {
16526         totalProperty: 'total',
16527         successProperty : 'success',
16528         root : 'data',
16529         id : 'id'
16530     });
16531     
16532     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16533 };
16534 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16535     
16536     readerType : 'Json',
16537     
16538     /**
16539      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16540      * Used by Store query builder to append _requestMeta to params.
16541      * 
16542      */
16543     metaFromRemote : false,
16544     /**
16545      * This method is only used by a DataProxy which has retrieved data from a remote server.
16546      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16547      * @return {Object} data A data block which is used by an Roo.data.Store object as
16548      * a cache of Roo.data.Records.
16549      */
16550     read : function(response){
16551         var json = response.responseText;
16552        
16553         var o = /* eval:var:o */ eval("("+json+")");
16554         if(!o) {
16555             throw {message: "JsonReader.read: Json object not found"};
16556         }
16557         
16558         if(o.metaData){
16559             
16560             delete this.ef;
16561             this.metaFromRemote = true;
16562             this.meta = o.metaData;
16563             this.recordType = Roo.data.Record.create(o.metaData.fields);
16564             this.onMetaChange(this.meta, this.recordType, o);
16565         }
16566         return this.readRecords(o);
16567     },
16568
16569     // private function a store will implement
16570     onMetaChange : function(meta, recordType, o){
16571
16572     },
16573
16574     /**
16575          * @ignore
16576          */
16577     simpleAccess: function(obj, subsc) {
16578         return obj[subsc];
16579     },
16580
16581         /**
16582          * @ignore
16583          */
16584     getJsonAccessor: function(){
16585         var re = /[\[\.]/;
16586         return function(expr) {
16587             try {
16588                 return(re.test(expr))
16589                     ? new Function("obj", "return obj." + expr)
16590                     : function(obj){
16591                         return obj[expr];
16592                     };
16593             } catch(e){}
16594             return Roo.emptyFn;
16595         };
16596     }(),
16597
16598     /**
16599      * Create a data block containing Roo.data.Records from an XML document.
16600      * @param {Object} o An object which contains an Array of row objects in the property specified
16601      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16602      * which contains the total size of the dataset.
16603      * @return {Object} data A data block which is used by an Roo.data.Store object as
16604      * a cache of Roo.data.Records.
16605      */
16606     readRecords : function(o){
16607         /**
16608          * After any data loads, the raw JSON data is available for further custom processing.
16609          * @type Object
16610          */
16611         this.o = o;
16612         var s = this.meta, Record = this.recordType,
16613             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16614
16615 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16616         if (!this.ef) {
16617             if(s.totalProperty) {
16618                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16619                 }
16620                 if(s.successProperty) {
16621                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16622                 }
16623                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16624                 if (s.id) {
16625                         var g = this.getJsonAccessor(s.id);
16626                         this.getId = function(rec) {
16627                                 var r = g(rec);  
16628                                 return (r === undefined || r === "") ? null : r;
16629                         };
16630                 } else {
16631                         this.getId = function(){return null;};
16632                 }
16633             this.ef = [];
16634             for(var jj = 0; jj < fl; jj++){
16635                 f = fi[jj];
16636                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16637                 this.ef[jj] = this.getJsonAccessor(map);
16638             }
16639         }
16640
16641         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16642         if(s.totalProperty){
16643             var vt = parseInt(this.getTotal(o), 10);
16644             if(!isNaN(vt)){
16645                 totalRecords = vt;
16646             }
16647         }
16648         if(s.successProperty){
16649             var vs = this.getSuccess(o);
16650             if(vs === false || vs === 'false'){
16651                 success = false;
16652             }
16653         }
16654         var records = [];
16655         for(var i = 0; i < c; i++){
16656                 var n = root[i];
16657             var values = {};
16658             var id = this.getId(n);
16659             for(var j = 0; j < fl; j++){
16660                 f = fi[j];
16661             var v = this.ef[j](n);
16662             if (!f.convert) {
16663                 Roo.log('missing convert for ' + f.name);
16664                 Roo.log(f);
16665                 continue;
16666             }
16667             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16668             }
16669             var record = new Record(values, id);
16670             record.json = n;
16671             records[i] = record;
16672         }
16673         return {
16674             raw : o,
16675             success : success,
16676             records : records,
16677             totalRecords : totalRecords
16678         };
16679     },
16680     // used when loading children.. @see loadDataFromChildren
16681     toLoadData: function(rec)
16682     {
16683         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16684         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16685         return { data : data, total : data.length };
16686         
16687     }
16688 });/*
16689  * Based on:
16690  * Ext JS Library 1.1.1
16691  * Copyright(c) 2006-2007, Ext JS, LLC.
16692  *
16693  * Originally Released Under LGPL - original licence link has changed is not relivant.
16694  *
16695  * Fork - LGPL
16696  * <script type="text/javascript">
16697  */
16698
16699 /**
16700  * @class Roo.data.ArrayReader
16701  * @extends Roo.data.DataReader
16702  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16703  * Each element of that Array represents a row of data fields. The
16704  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16705  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16706  * <p>
16707  * Example code:.
16708  * <pre><code>
16709 var RecordDef = Roo.data.Record.create([
16710     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16711     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16712 ]);
16713 var myReader = new Roo.data.ArrayReader({
16714     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16715 }, RecordDef);
16716 </code></pre>
16717  * <p>
16718  * This would consume an Array like this:
16719  * <pre><code>
16720 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16721   </code></pre>
16722  
16723  * @constructor
16724  * Create a new JsonReader
16725  * @param {Object} meta Metadata configuration options.
16726  * @param {Object|Array} recordType Either an Array of field definition objects
16727  * 
16728  * @cfg {Array} fields Array of field definition objects
16729  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16730  * as specified to {@link Roo.data.Record#create},
16731  * or an {@link Roo.data.Record} object
16732  *
16733  * 
16734  * created using {@link Roo.data.Record#create}.
16735  */
16736 Roo.data.ArrayReader = function(meta, recordType)
16737 {    
16738     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16739 };
16740
16741 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16742     
16743       /**
16744      * Create a data block containing Roo.data.Records from an XML document.
16745      * @param {Object} o An Array of row objects which represents the dataset.
16746      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16747      * a cache of Roo.data.Records.
16748      */
16749     readRecords : function(o)
16750     {
16751         var sid = this.meta ? this.meta.id : null;
16752         var recordType = this.recordType, fields = recordType.prototype.fields;
16753         var records = [];
16754         var root = o;
16755         for(var i = 0; i < root.length; i++){
16756             var n = root[i];
16757             var values = {};
16758             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16759             for(var j = 0, jlen = fields.length; j < jlen; j++){
16760                 var f = fields.items[j];
16761                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16762                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16763                 v = f.convert(v);
16764                 values[f.name] = v;
16765             }
16766             var record = new recordType(values, id);
16767             record.json = n;
16768             records[records.length] = record;
16769         }
16770         return {
16771             records : records,
16772             totalRecords : records.length
16773         };
16774     },
16775     // used when loading children.. @see loadDataFromChildren
16776     toLoadData: function(rec)
16777     {
16778         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16779         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16780         
16781     }
16782     
16783     
16784 });/*
16785  * - LGPL
16786  * * 
16787  */
16788
16789 /**
16790  * @class Roo.bootstrap.form.ComboBox
16791  * @extends Roo.bootstrap.form.TriggerField
16792  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16793  * @cfg {Boolean} append (true|false) default false
16794  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16795  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16796  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16797  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16798  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16799  * @cfg {Boolean} animate default true
16800  * @cfg {Boolean} emptyResultText only for touch device
16801  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16802  * @cfg {String} emptyTitle default ''
16803  * @cfg {Number} width fixed with? experimental
16804  * @constructor
16805  * Create a new ComboBox.
16806  * @param {Object} config Configuration options
16807  */
16808 Roo.bootstrap.form.ComboBox = function(config){
16809     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16810     this.addEvents({
16811         /**
16812          * @event expand
16813          * Fires when the dropdown list is expanded
16814         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16815         */
16816         'expand' : true,
16817         /**
16818          * @event collapse
16819          * Fires when the dropdown list is collapsed
16820         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16821         */
16822         'collapse' : true,
16823         /**
16824          * @event beforeselect
16825          * Fires before a list item is selected. Return false to cancel the selection.
16826         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16827         * @param {Roo.data.Record} record The data record returned from the underlying store
16828         * @param {Number} index The index of the selected item in the dropdown list
16829         */
16830         'beforeselect' : true,
16831         /**
16832          * @event select
16833          * Fires when a list item is selected
16834         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16835         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16836         * @param {Number} index The index of the selected item in the dropdown list
16837         */
16838         'select' : true,
16839         /**
16840          * @event beforequery
16841          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16842          * The event object passed has these properties:
16843         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16844         * @param {String} query The query
16845         * @param {Boolean} forceAll true to force "all" query
16846         * @param {Boolean} cancel true to cancel the query
16847         * @param {Object} e The query event object
16848         */
16849         'beforequery': true,
16850          /**
16851          * @event add
16852          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16853         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16854         */
16855         'add' : true,
16856         /**
16857          * @event edit
16858          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16859         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16860         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16861         */
16862         'edit' : true,
16863         /**
16864          * @event remove
16865          * Fires when the remove value from the combobox array
16866         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16867         */
16868         'remove' : true,
16869         /**
16870          * @event afterremove
16871          * Fires when the remove value from the combobox array
16872         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16873         */
16874         'afterremove' : true,
16875         /**
16876          * @event specialfilter
16877          * Fires when specialfilter
16878             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16879             */
16880         'specialfilter' : true,
16881         /**
16882          * @event tick
16883          * Fires when tick the element
16884             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16885             */
16886         'tick' : true,
16887         /**
16888          * @event touchviewdisplay
16889          * Fires when touch view require special display (default is using displayField)
16890             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16891             * @param {Object} cfg set html .
16892             */
16893         'touchviewdisplay' : true
16894         
16895     });
16896     
16897     this.item = [];
16898     this.tickItems = [];
16899     
16900     this.selectedIndex = -1;
16901     if(this.mode == 'local'){
16902         if(config.queryDelay === undefined){
16903             this.queryDelay = 10;
16904         }
16905         if(config.minChars === undefined){
16906             this.minChars = 0;
16907         }
16908     }
16909 };
16910
16911 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16912      
16913     /**
16914      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16915      * rendering into an Roo.Editor, defaults to false)
16916      */
16917     /**
16918      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16919      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16920      */
16921     /**
16922      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16923      */
16924     /**
16925      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16926      * the dropdown list (defaults to undefined, with no header element)
16927      */
16928
16929      /**
16930      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16931      */
16932      
16933      /**
16934      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16935      */
16936     listWidth: undefined,
16937     /**
16938      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16939      * mode = 'remote' or 'text' if mode = 'local')
16940      */
16941     displayField: undefined,
16942     
16943     /**
16944      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16945      * mode = 'remote' or 'value' if mode = 'local'). 
16946      * Note: use of a valueField requires the user make a selection
16947      * in order for a value to be mapped.
16948      */
16949     valueField: undefined,
16950     /**
16951      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16952      */
16953     modalTitle : '',
16954     
16955     /**
16956      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16957      * field's data value (defaults to the underlying DOM element's name)
16958      */
16959     hiddenName: undefined,
16960     /**
16961      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16962      */
16963     listClass: '',
16964     /**
16965      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16966      */
16967     selectedClass: 'active',
16968     
16969     /**
16970      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16971      */
16972     shadow:'sides',
16973     /**
16974      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16975      * anchor positions (defaults to 'tl-bl')
16976      */
16977     listAlign: 'tl-bl?',
16978     /**
16979      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16980      */
16981     maxHeight: 300,
16982     /**
16983      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16984      * query specified by the allQuery config option (defaults to 'query')
16985      */
16986     triggerAction: 'query',
16987     /**
16988      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16989      * (defaults to 4, does not apply if editable = false)
16990      */
16991     minChars : 4,
16992     /**
16993      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16994      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16995      */
16996     typeAhead: false,
16997     /**
16998      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16999      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17000      */
17001     queryDelay: 500,
17002     /**
17003      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17004      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17005      */
17006     pageSize: 0,
17007     /**
17008      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17009      * when editable = true (defaults to false)
17010      */
17011     selectOnFocus:false,
17012     /**
17013      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17014      */
17015     queryParam: 'query',
17016     /**
17017      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17018      * when mode = 'remote' (defaults to 'Loading...')
17019      */
17020     loadingText: 'Loading...',
17021     /**
17022      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17023      */
17024     resizable: false,
17025     /**
17026      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17027      */
17028     handleHeight : 8,
17029     /**
17030      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17031      * traditional select (defaults to true)
17032      */
17033     editable: true,
17034     /**
17035      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17036      */
17037     allQuery: '',
17038     /**
17039      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17040      */
17041     mode: 'remote',
17042     /**
17043      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17044      * listWidth has a higher value)
17045      */
17046     minListWidth : 70,
17047     /**
17048      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17049      * allow the user to set arbitrary text into the field (defaults to false)
17050      */
17051     forceSelection:false,
17052     /**
17053      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17054      * if typeAhead = true (defaults to 250)
17055      */
17056     typeAheadDelay : 250,
17057     /**
17058      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17059      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17060      */
17061     valueNotFoundText : undefined,
17062     /**
17063      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17064      */
17065     blockFocus : false,
17066     
17067     /**
17068      * @cfg {Boolean} disableClear Disable showing of clear button.
17069      */
17070     disableClear : false,
17071     /**
17072      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17073      */
17074     alwaysQuery : false,
17075     
17076     /**
17077      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17078      */
17079     multiple : false,
17080     
17081     /**
17082      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17083      */
17084     invalidClass : "has-warning",
17085     
17086     /**
17087      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17088      */
17089     validClass : "has-success",
17090     
17091     /**
17092      * @cfg {Boolean} specialFilter (true|false) special filter default false
17093      */
17094     specialFilter : false,
17095     
17096     /**
17097      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17098      */
17099     mobileTouchView : true,
17100     
17101     /**
17102      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17103      */
17104     useNativeIOS : false,
17105     
17106     /**
17107      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17108      */
17109     mobile_restrict_height : false,
17110     
17111     ios_options : false,
17112     
17113     //private
17114     addicon : false,
17115     editicon: false,
17116     
17117     page: 0,
17118     hasQuery: false,
17119     append: false,
17120     loadNext: false,
17121     autoFocus : true,
17122     tickable : false,
17123     btnPosition : 'right',
17124     triggerList : true,
17125     showToggleBtn : true,
17126     animate : true,
17127     emptyResultText: 'Empty',
17128     triggerText : 'Select',
17129     emptyTitle : '',
17130     width : false,
17131     
17132     // element that contains real text value.. (when hidden is used..)
17133     
17134     getAutoCreate : function()
17135     {   
17136         var cfg = false;
17137         //render
17138         /*
17139          * Render classic select for iso
17140          */
17141         
17142         if(Roo.isIOS && this.useNativeIOS){
17143             cfg = this.getAutoCreateNativeIOS();
17144             return cfg;
17145         }
17146         
17147         /*
17148          * Touch Devices
17149          */
17150         
17151         if(Roo.isTouch && this.mobileTouchView){
17152             cfg = this.getAutoCreateTouchView();
17153             return cfg;;
17154         }
17155         
17156         /*
17157          *  Normal ComboBox
17158          */
17159         if(!this.tickable){
17160             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17161             return cfg;
17162         }
17163         
17164         /*
17165          *  ComboBox with tickable selections
17166          */
17167              
17168         var align = this.labelAlign || this.parentLabelAlign();
17169         
17170         cfg = {
17171             cls : 'form-group roo-combobox-tickable' //input-group
17172         };
17173         
17174         var btn_text_select = '';
17175         var btn_text_done = '';
17176         var btn_text_cancel = '';
17177         
17178         if (this.btn_text_show) {
17179             btn_text_select = 'Select';
17180             btn_text_done = 'Done';
17181             btn_text_cancel = 'Cancel'; 
17182         }
17183         
17184         var buttons = {
17185             tag : 'div',
17186             cls : 'tickable-buttons',
17187             cn : [
17188                 {
17189                     tag : 'button',
17190                     type : 'button',
17191                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17192                     //html : this.triggerText
17193                     html: btn_text_select
17194                 },
17195                 {
17196                     tag : 'button',
17197                     type : 'button',
17198                     name : 'ok',
17199                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17200                     //html : 'Done'
17201                     html: btn_text_done
17202                 },
17203                 {
17204                     tag : 'button',
17205                     type : 'button',
17206                     name : 'cancel',
17207                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17208                     //html : 'Cancel'
17209                     html: btn_text_cancel
17210                 }
17211             ]
17212         };
17213         
17214         if(this.editable){
17215             buttons.cn.unshift({
17216                 tag: 'input',
17217                 cls: 'roo-select2-search-field-input'
17218             });
17219         }
17220         
17221         var _this = this;
17222         
17223         Roo.each(buttons.cn, function(c){
17224             if (_this.size) {
17225                 c.cls += ' btn-' + _this.size;
17226             }
17227
17228             if (_this.disabled) {
17229                 c.disabled = true;
17230             }
17231         });
17232         
17233         var box = {
17234             tag: 'div',
17235             style : 'display: contents',
17236             cn: [
17237                 {
17238                     tag: 'input',
17239                     type : 'hidden',
17240                     cls: 'form-hidden-field'
17241                 },
17242                 {
17243                     tag: 'ul',
17244                     cls: 'roo-select2-choices',
17245                     cn:[
17246                         {
17247                             tag: 'li',
17248                             cls: 'roo-select2-search-field',
17249                             cn: [
17250                                 buttons
17251                             ]
17252                         }
17253                     ]
17254                 }
17255             ]
17256         };
17257         
17258         var combobox = {
17259             cls: 'roo-select2-container input-group roo-select2-container-multi',
17260             cn: [
17261                 
17262                 box
17263 //                {
17264 //                    tag: 'ul',
17265 //                    cls: 'typeahead typeahead-long dropdown-menu',
17266 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17267 //                }
17268             ]
17269         };
17270         
17271         if(this.hasFeedback && !this.allowBlank){
17272             
17273             var feedback = {
17274                 tag: 'span',
17275                 cls: 'glyphicon form-control-feedback'
17276             };
17277
17278             combobox.cn.push(feedback);
17279         }
17280         
17281         
17282         
17283         var indicator = {
17284             tag : 'i',
17285             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17286             tooltip : 'This field is required'
17287         };
17288         if (Roo.bootstrap.version == 4) {
17289             indicator = {
17290                 tag : 'i',
17291                 style : 'display:none'
17292             };
17293         }
17294         if (align ==='left' && this.fieldLabel.length) {
17295             
17296             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17297             
17298             cfg.cn = [
17299                 indicator,
17300                 {
17301                     tag: 'label',
17302                     'for' :  id,
17303                     cls : 'control-label col-form-label',
17304                     html : this.fieldLabel
17305
17306                 },
17307                 {
17308                     cls : "", 
17309                     cn: [
17310                         combobox
17311                     ]
17312                 }
17313
17314             ];
17315             
17316             var labelCfg = cfg.cn[1];
17317             var contentCfg = cfg.cn[2];
17318             
17319
17320             if(this.indicatorpos == 'right'){
17321                 
17322                 cfg.cn = [
17323                     {
17324                         tag: 'label',
17325                         'for' :  id,
17326                         cls : 'control-label col-form-label',
17327                         cn : [
17328                             {
17329                                 tag : 'span',
17330                                 html : this.fieldLabel
17331                             },
17332                             indicator
17333                         ]
17334                     },
17335                     {
17336                         cls : "",
17337                         cn: [
17338                             combobox
17339                         ]
17340                     }
17341
17342                 ];
17343                 
17344                 
17345                 
17346                 labelCfg = cfg.cn[0];
17347                 contentCfg = cfg.cn[1];
17348             
17349             }
17350             
17351             if(this.labelWidth > 12){
17352                 labelCfg.style = "width: " + this.labelWidth + 'px';
17353             }
17354             if(this.width * 1 > 0){
17355                 contentCfg.style = "width: " + this.width + 'px';
17356             }
17357             if(this.labelWidth < 13 && this.labelmd == 0){
17358                 this.labelmd = this.labelWidth;
17359             }
17360             
17361             if(this.labellg > 0){
17362                 labelCfg.cls += ' col-lg-' + this.labellg;
17363                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17364             }
17365             
17366             if(this.labelmd > 0){
17367                 labelCfg.cls += ' col-md-' + this.labelmd;
17368                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17369             }
17370             
17371             if(this.labelsm > 0){
17372                 labelCfg.cls += ' col-sm-' + this.labelsm;
17373                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17374             }
17375             
17376             if(this.labelxs > 0){
17377                 labelCfg.cls += ' col-xs-' + this.labelxs;
17378                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17379             }
17380                 
17381                 
17382         } else if ( this.fieldLabel.length) {
17383 //                Roo.log(" label");
17384                  cfg.cn = [
17385                    indicator,
17386                     {
17387                         tag: 'label',
17388                         //cls : 'input-group-addon',
17389                         html : this.fieldLabel
17390                     },
17391                     combobox
17392                 ];
17393                 
17394                 if(this.indicatorpos == 'right'){
17395                     cfg.cn = [
17396                         {
17397                             tag: 'label',
17398                             //cls : 'input-group-addon',
17399                             html : this.fieldLabel
17400                         },
17401                         indicator,
17402                         combobox
17403                     ];
17404                     
17405                 }
17406
17407         } else {
17408             
17409 //                Roo.log(" no label && no align");
17410                 cfg = combobox
17411                      
17412                 
17413         }
17414          
17415         var settings=this;
17416         ['xs','sm','md','lg'].map(function(size){
17417             if (settings[size]) {
17418                 cfg.cls += ' col-' + size + '-' + settings[size];
17419             }
17420         });
17421         
17422         return cfg;
17423         
17424     },
17425     
17426     _initEventsCalled : false,
17427     
17428     // private
17429     initEvents: function()
17430     {   
17431         if (this._initEventsCalled) { // as we call render... prevent looping...
17432             return;
17433         }
17434         this._initEventsCalled = true;
17435         
17436         if (!this.store) {
17437             throw "can not find store for combo";
17438         }
17439         
17440         this.indicator = this.indicatorEl();
17441         
17442         this.store = Roo.factory(this.store, Roo.data);
17443         this.store.parent = this;
17444         
17445         // if we are building from html. then this element is so complex, that we can not really
17446         // use the rendered HTML.
17447         // so we have to trash and replace the previous code.
17448         if (Roo.XComponent.build_from_html) {
17449             // remove this element....
17450             var e = this.el.dom, k=0;
17451             while (e ) { e = e.previousSibling;  ++k;}
17452
17453             this.el.remove();
17454             
17455             this.el=false;
17456             this.rendered = false;
17457             
17458             this.render(this.parent().getChildContainer(true), k);
17459         }
17460         
17461         if(Roo.isIOS && this.useNativeIOS){
17462             this.initIOSView();
17463             return;
17464         }
17465         
17466         /*
17467          * Touch Devices
17468          */
17469         
17470         if(Roo.isTouch && this.mobileTouchView){
17471             this.initTouchView();
17472             return;
17473         }
17474         
17475         if(this.tickable){
17476             this.initTickableEvents();
17477             return;
17478         }
17479         
17480         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17481         
17482         if(this.hiddenName){
17483             
17484             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17485             
17486             this.hiddenField.dom.value =
17487                 this.hiddenValue !== undefined ? this.hiddenValue :
17488                 this.value !== undefined ? this.value : '';
17489
17490             // prevent input submission
17491             this.el.dom.removeAttribute('name');
17492             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17493              
17494              
17495         }
17496         //if(Roo.isGecko){
17497         //    this.el.dom.setAttribute('autocomplete', 'off');
17498         //}
17499         
17500         var cls = 'x-combo-list';
17501         
17502         //this.list = new Roo.Layer({
17503         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17504         //});
17505         
17506         var _this = this;
17507         
17508         (function(){
17509             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17510             _this.list.setWidth(lw);
17511         }).defer(100);
17512         
17513         this.list.on('mouseover', this.onViewOver, this);
17514         this.list.on('mousemove', this.onViewMove, this);
17515         this.list.on('scroll', this.onViewScroll, this);
17516         
17517         /*
17518         this.list.swallowEvent('mousewheel');
17519         this.assetHeight = 0;
17520
17521         if(this.title){
17522             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17523             this.assetHeight += this.header.getHeight();
17524         }
17525
17526         this.innerList = this.list.createChild({cls:cls+'-inner'});
17527         this.innerList.on('mouseover', this.onViewOver, this);
17528         this.innerList.on('mousemove', this.onViewMove, this);
17529         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17530         
17531         if(this.allowBlank && !this.pageSize && !this.disableClear){
17532             this.footer = this.list.createChild({cls:cls+'-ft'});
17533             this.pageTb = new Roo.Toolbar(this.footer);
17534            
17535         }
17536         if(this.pageSize){
17537             this.footer = this.list.createChild({cls:cls+'-ft'});
17538             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17539                     {pageSize: this.pageSize});
17540             
17541         }
17542         
17543         if (this.pageTb && this.allowBlank && !this.disableClear) {
17544             var _this = this;
17545             this.pageTb.add(new Roo.Toolbar.Fill(), {
17546                 cls: 'x-btn-icon x-btn-clear',
17547                 text: '&#160;',
17548                 handler: function()
17549                 {
17550                     _this.collapse();
17551                     _this.clearValue();
17552                     _this.onSelect(false, -1);
17553                 }
17554             });
17555         }
17556         if (this.footer) {
17557             this.assetHeight += this.footer.getHeight();
17558         }
17559         */
17560             
17561         if(!this.tpl){
17562             this.tpl = Roo.bootstrap.version == 4 ?
17563                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17564                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17565         }
17566
17567         this.view = new Roo.View(this.list, this.tpl, {
17568             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17569         });
17570         //this.view.wrapEl.setDisplayed(false);
17571         this.view.on('click', this.onViewClick, this);
17572         
17573         
17574         this.store.on('beforeload', this.onBeforeLoad, this);
17575         this.store.on('load', this.onLoad, this);
17576         this.store.on('loadexception', this.onLoadException, this);
17577         /*
17578         if(this.resizable){
17579             this.resizer = new Roo.Resizable(this.list,  {
17580                pinned:true, handles:'se'
17581             });
17582             this.resizer.on('resize', function(r, w, h){
17583                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17584                 this.listWidth = w;
17585                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17586                 this.restrictHeight();
17587             }, this);
17588             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17589         }
17590         */
17591         if(!this.editable){
17592             this.editable = true;
17593             this.setEditable(false);
17594         }
17595         
17596         /*
17597         
17598         if (typeof(this.events.add.listeners) != 'undefined') {
17599             
17600             this.addicon = this.wrap.createChild(
17601                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17602        
17603             this.addicon.on('click', function(e) {
17604                 this.fireEvent('add', this);
17605             }, this);
17606         }
17607         if (typeof(this.events.edit.listeners) != 'undefined') {
17608             
17609             this.editicon = this.wrap.createChild(
17610                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17611             if (this.addicon) {
17612                 this.editicon.setStyle('margin-left', '40px');
17613             }
17614             this.editicon.on('click', function(e) {
17615                 
17616                 // we fire even  if inothing is selected..
17617                 this.fireEvent('edit', this, this.lastData );
17618                 
17619             }, this);
17620         }
17621         */
17622         
17623         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17624             "up" : function(e){
17625                 this.inKeyMode = true;
17626                 this.selectPrev();
17627             },
17628
17629             "down" : function(e){
17630                 if(!this.isExpanded()){
17631                     this.onTriggerClick();
17632                 }else{
17633                     this.inKeyMode = true;
17634                     this.selectNext();
17635                 }
17636             },
17637
17638             "enter" : function(e){
17639 //                this.onViewClick();
17640                 //return true;
17641                 this.collapse();
17642                 
17643                 if(this.fireEvent("specialkey", this, e)){
17644                     this.onViewClick(false);
17645                 }
17646                 
17647                 return true;
17648             },
17649
17650             "esc" : function(e){
17651                 this.collapse();
17652             },
17653
17654             "tab" : function(e){
17655                 this.collapse();
17656                 
17657                 if(this.fireEvent("specialkey", this, e)){
17658                     this.onViewClick(false);
17659                 }
17660                 
17661                 return true;
17662             },
17663
17664             scope : this,
17665
17666             doRelay : function(foo, bar, hname){
17667                 if(hname == 'down' || this.scope.isExpanded()){
17668                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17669                 }
17670                 return true;
17671             },
17672
17673             forceKeyDown: true
17674         });
17675         
17676         
17677         this.queryDelay = Math.max(this.queryDelay || 10,
17678                 this.mode == 'local' ? 10 : 250);
17679         
17680         
17681         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17682         
17683         if(this.typeAhead){
17684             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17685         }
17686         if(this.editable !== false){
17687             this.inputEl().on("keyup", this.onKeyUp, this);
17688         }
17689         if(this.forceSelection){
17690             this.inputEl().on('blur', this.doForce, this);
17691         }
17692         
17693         if(this.multiple){
17694             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17695             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17696         }
17697     },
17698     
17699     initTickableEvents: function()
17700     {   
17701         this.createList();
17702         
17703         if(this.hiddenName){
17704             
17705             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17706             
17707             this.hiddenField.dom.value =
17708                 this.hiddenValue !== undefined ? this.hiddenValue :
17709                 this.value !== undefined ? this.value : '';
17710
17711             // prevent input submission
17712             this.el.dom.removeAttribute('name');
17713             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17714              
17715              
17716         }
17717         
17718 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17719         
17720         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17721         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17722         if(this.triggerList){
17723             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17724         }
17725          
17726         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17727         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17728         
17729         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17730         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17731         
17732         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17733         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17734         
17735         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17736         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17737         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17738         
17739         this.okBtn.hide();
17740         this.cancelBtn.hide();
17741         
17742         var _this = this;
17743         
17744         (function(){
17745             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17746             _this.list.setWidth(lw);
17747         }).defer(100);
17748         
17749         this.list.on('mouseover', this.onViewOver, this);
17750         this.list.on('mousemove', this.onViewMove, this);
17751         
17752         this.list.on('scroll', this.onViewScroll, this);
17753         
17754         if(!this.tpl){
17755             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17756                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17757         }
17758
17759         this.view = new Roo.View(this.list, this.tpl, {
17760             singleSelect:true,
17761             tickable:true,
17762             parent:this,
17763             store: this.store,
17764             selectedClass: this.selectedClass
17765         });
17766         
17767         //this.view.wrapEl.setDisplayed(false);
17768         this.view.on('click', this.onViewClick, this);
17769         
17770         
17771         
17772         this.store.on('beforeload', this.onBeforeLoad, this);
17773         this.store.on('load', this.onLoad, this);
17774         this.store.on('loadexception', this.onLoadException, this);
17775         
17776         if(this.editable){
17777             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17778                 "up" : function(e){
17779                     this.inKeyMode = true;
17780                     this.selectPrev();
17781                 },
17782
17783                 "down" : function(e){
17784                     this.inKeyMode = true;
17785                     this.selectNext();
17786                 },
17787
17788                 "enter" : function(e){
17789                     if(this.fireEvent("specialkey", this, e)){
17790                         this.onViewClick(false);
17791                     }
17792                     
17793                     return true;
17794                 },
17795
17796                 "esc" : function(e){
17797                     this.onTickableFooterButtonClick(e, false, false);
17798                 },
17799
17800                 "tab" : function(e){
17801                     this.fireEvent("specialkey", this, e);
17802                     
17803                     this.onTickableFooterButtonClick(e, false, false);
17804                     
17805                     return true;
17806                 },
17807
17808                 scope : this,
17809
17810                 doRelay : function(e, fn, key){
17811                     if(this.scope.isExpanded()){
17812                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17813                     }
17814                     return true;
17815                 },
17816
17817                 forceKeyDown: true
17818             });
17819         }
17820         
17821         this.queryDelay = Math.max(this.queryDelay || 10,
17822                 this.mode == 'local' ? 10 : 250);
17823         
17824         
17825         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17826         
17827         if(this.typeAhead){
17828             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17829         }
17830         
17831         if(this.editable !== false){
17832             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17833         }
17834         
17835         this.indicator = this.indicatorEl();
17836         
17837         if(this.indicator){
17838             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17839             this.indicator.hide();
17840         }
17841         
17842     },
17843
17844     onDestroy : function(){
17845         if(this.view){
17846             this.view.setStore(null);
17847             this.view.el.removeAllListeners();
17848             this.view.el.remove();
17849             this.view.purgeListeners();
17850         }
17851         if(this.list){
17852             this.list.dom.innerHTML  = '';
17853         }
17854         
17855         if(this.store){
17856             this.store.un('beforeload', this.onBeforeLoad, this);
17857             this.store.un('load', this.onLoad, this);
17858             this.store.un('loadexception', this.onLoadException, this);
17859         }
17860         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17861     },
17862
17863     // private
17864     fireKey : function(e){
17865         if(e.isNavKeyPress() && !this.list.isVisible()){
17866             this.fireEvent("specialkey", this, e);
17867         }
17868     },
17869
17870     // private
17871     onResize: function(w, h)
17872     {
17873         
17874         
17875 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17876 //        
17877 //        if(typeof w != 'number'){
17878 //            // we do not handle it!?!?
17879 //            return;
17880 //        }
17881 //        var tw = this.trigger.getWidth();
17882 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17883 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17884 //        var x = w - tw;
17885 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17886 //            
17887 //        //this.trigger.setStyle('left', x+'px');
17888 //        
17889 //        if(this.list && this.listWidth === undefined){
17890 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17891 //            this.list.setWidth(lw);
17892 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17893 //        }
17894         
17895     
17896         
17897     },
17898
17899     /**
17900      * Allow or prevent the user from directly editing the field text.  If false is passed,
17901      * the user will only be able to select from the items defined in the dropdown list.  This method
17902      * is the runtime equivalent of setting the 'editable' config option at config time.
17903      * @param {Boolean} value True to allow the user to directly edit the field text
17904      */
17905     setEditable : function(value){
17906         if(value == this.editable){
17907             return;
17908         }
17909         this.editable = value;
17910         if(!value){
17911             this.inputEl().dom.setAttribute('readOnly', true);
17912             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17913             this.inputEl().addClass('x-combo-noedit');
17914         }else{
17915             this.inputEl().dom.removeAttribute('readOnly');
17916             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17917             this.inputEl().removeClass('x-combo-noedit');
17918         }
17919     },
17920
17921     // private
17922     
17923     onBeforeLoad : function(combo,opts){
17924         if(!this.hasFocus){
17925             return;
17926         }
17927          if (!opts.add) {
17928             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17929          }
17930         this.restrictHeight();
17931         this.selectedIndex = -1;
17932     },
17933
17934     // private
17935     onLoad : function(){
17936         
17937         this.hasQuery = false;
17938         
17939         if(!this.hasFocus){
17940             return;
17941         }
17942         
17943         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17944             this.loading.hide();
17945         }
17946         
17947         if(this.store.getCount() > 0){
17948             
17949             this.expand();
17950             this.restrictHeight();
17951             if(this.lastQuery == this.allQuery){
17952                 if(this.editable && !this.tickable){
17953                     this.inputEl().dom.select();
17954                 }
17955                 
17956                 if(
17957                     !this.selectByValue(this.value, true) &&
17958                     this.autoFocus && 
17959                     (
17960                         !this.store.lastOptions ||
17961                         typeof(this.store.lastOptions.add) == 'undefined' || 
17962                         this.store.lastOptions.add != true
17963                     )
17964                 ){
17965                     this.select(0, true);
17966                 }
17967             }else{
17968                 if(this.autoFocus){
17969                     this.selectNext();
17970                 }
17971                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17972                     this.taTask.delay(this.typeAheadDelay);
17973                 }
17974             }
17975         }else{
17976             this.onEmptyResults();
17977         }
17978         
17979         //this.el.focus();
17980     },
17981     // private
17982     onLoadException : function()
17983     {
17984         this.hasQuery = false;
17985         
17986         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17987             this.loading.hide();
17988         }
17989         
17990         if(this.tickable && this.editable){
17991             return;
17992         }
17993         
17994         this.collapse();
17995         // only causes errors at present
17996         //Roo.log(this.store.reader.jsonData);
17997         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17998             // fixme
17999             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18000         //}
18001         
18002         
18003     },
18004     // private
18005     onTypeAhead : function(){
18006         if(this.store.getCount() > 0){
18007             var r = this.store.getAt(0);
18008             var newValue = r.data[this.displayField];
18009             var len = newValue.length;
18010             var selStart = this.getRawValue().length;
18011             
18012             if(selStart != len){
18013                 this.setRawValue(newValue);
18014                 this.selectText(selStart, newValue.length);
18015             }
18016         }
18017     },
18018
18019     // private
18020     onSelect : function(record, index){
18021         
18022         if(this.fireEvent('beforeselect', this, record, index) !== false){
18023         
18024             this.setFromData(index > -1 ? record.data : false);
18025             
18026             this.collapse();
18027             this.fireEvent('select', this, record, index);
18028         }
18029     },
18030
18031     /**
18032      * Returns the currently selected field value or empty string if no value is set.
18033      * @return {String} value The selected value
18034      */
18035     getValue : function()
18036     {
18037         if(Roo.isIOS && this.useNativeIOS){
18038             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18039         }
18040         
18041         if(this.multiple){
18042             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18043         }
18044         
18045         if(this.valueField){
18046             return typeof this.value != 'undefined' ? this.value : '';
18047         }else{
18048             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18049         }
18050     },
18051     
18052     getRawValue : function()
18053     {
18054         if(Roo.isIOS && this.useNativeIOS){
18055             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18056         }
18057         
18058         var v = this.inputEl().getValue();
18059         
18060         return v;
18061     },
18062
18063     /**
18064      * Clears any text/value currently set in the field
18065      */
18066     clearValue : function(){
18067         
18068         if(this.hiddenField){
18069             this.hiddenField.dom.value = '';
18070         }
18071         this.value = '';
18072         this.setRawValue('');
18073         this.lastSelectionText = '';
18074         this.lastData = false;
18075         
18076         var close = this.closeTriggerEl();
18077         
18078         if(close){
18079             close.hide();
18080         }
18081         
18082         this.validate();
18083         
18084     },
18085
18086     /**
18087      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18088      * will be displayed in the field.  If the value does not match the data value of an existing item,
18089      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18090      * Otherwise the field will be blank (although the value will still be set).
18091      * @param {String} value The value to match
18092      */
18093     setValue : function(v)
18094     {
18095         if(Roo.isIOS && this.useNativeIOS){
18096             this.setIOSValue(v);
18097             return;
18098         }
18099         
18100         if(this.multiple){
18101             this.syncValue();
18102             return;
18103         }
18104         
18105         var text = v;
18106         if(this.valueField){
18107             var r = this.findRecord(this.valueField, v);
18108             if(r){
18109                 text = r.data[this.displayField];
18110             }else if(this.valueNotFoundText !== undefined){
18111                 text = this.valueNotFoundText;
18112             }
18113         }
18114         this.lastSelectionText = text;
18115         if(this.hiddenField){
18116             this.hiddenField.dom.value = v;
18117         }
18118         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18119         this.value = v;
18120         
18121         var close = this.closeTriggerEl();
18122         
18123         if(close){
18124             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18125         }
18126         
18127         this.validate();
18128     },
18129     /**
18130      * @property {Object} the last set data for the element
18131      */
18132     
18133     lastData : false,
18134     /**
18135      * Sets the value of the field based on a object which is related to the record format for the store.
18136      * @param {Object} value the value to set as. or false on reset?
18137      */
18138     setFromData : function(o){
18139         
18140         if(this.multiple){
18141             this.addItem(o);
18142             return;
18143         }
18144             
18145         var dv = ''; // display value
18146         var vv = ''; // value value..
18147         this.lastData = o;
18148         if (this.displayField) {
18149             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18150         } else {
18151             // this is an error condition!!!
18152             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18153         }
18154         
18155         if(this.valueField){
18156             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18157         }
18158         
18159         var close = this.closeTriggerEl();
18160         
18161         if(close){
18162             if(dv.length || vv * 1 > 0){
18163                 close.show() ;
18164                 this.blockFocus=true;
18165             } else {
18166                 close.hide();
18167             }             
18168         }
18169         
18170         if(this.hiddenField){
18171             this.hiddenField.dom.value = vv;
18172             
18173             this.lastSelectionText = dv;
18174             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18175             this.value = vv;
18176             return;
18177         }
18178         // no hidden field.. - we store the value in 'value', but still display
18179         // display field!!!!
18180         this.lastSelectionText = dv;
18181         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18182         this.value = vv;
18183         
18184         
18185         
18186     },
18187     // private
18188     reset : function(){
18189         // overridden so that last data is reset..
18190         
18191         if(this.multiple){
18192             this.clearItem();
18193             return;
18194         }
18195         
18196         this.setValue(this.originalValue);
18197         //this.clearInvalid();
18198         this.lastData = false;
18199         if (this.view) {
18200             this.view.clearSelections();
18201         }
18202         
18203         this.validate();
18204     },
18205     // private
18206     findRecord : function(prop, value){
18207         var record;
18208         if(this.store.getCount() > 0){
18209             this.store.each(function(r){
18210                 if(r.data[prop] == value){
18211                     record = r;
18212                     return false;
18213                 }
18214                 return true;
18215             });
18216         }
18217         return record;
18218     },
18219     
18220     getName: function()
18221     {
18222         // returns hidden if it's set..
18223         if (!this.rendered) {return ''};
18224         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18225         
18226     },
18227     // private
18228     onViewMove : function(e, t){
18229         this.inKeyMode = false;
18230     },
18231
18232     // private
18233     onViewOver : function(e, t){
18234         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18235             return;
18236         }
18237         var item = this.view.findItemFromChild(t);
18238         
18239         if(item){
18240             var index = this.view.indexOf(item);
18241             this.select(index, false);
18242         }
18243     },
18244
18245     // private
18246     onViewClick : function(view, doFocus, el, e)
18247     {
18248         var index = this.view.getSelectedIndexes()[0];
18249         
18250         var r = this.store.getAt(index);
18251         
18252         if(this.tickable){
18253             
18254             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18255                 return;
18256             }
18257             
18258             var rm = false;
18259             var _this = this;
18260             
18261             Roo.each(this.tickItems, function(v,k){
18262                 
18263                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18264                     Roo.log(v);
18265                     _this.tickItems.splice(k, 1);
18266                     
18267                     if(typeof(e) == 'undefined' && view == false){
18268                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18269                     }
18270                     
18271                     rm = true;
18272                     return;
18273                 }
18274             });
18275             
18276             if(rm){
18277                 return;
18278             }
18279             
18280             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18281                 this.tickItems.push(r.data);
18282             }
18283             
18284             if(typeof(e) == 'undefined' && view == false){
18285                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18286             }
18287                     
18288             return;
18289         }
18290         
18291         if(r){
18292             this.onSelect(r, index);
18293         }
18294         if(doFocus !== false && !this.blockFocus){
18295             this.inputEl().focus();
18296         }
18297     },
18298
18299     // private
18300     restrictHeight : function(){
18301         //this.innerList.dom.style.height = '';
18302         //var inner = this.innerList.dom;
18303         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18304         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18305         //this.list.beginUpdate();
18306         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18307         this.list.alignTo(this.inputEl(), this.listAlign);
18308         this.list.alignTo(this.inputEl(), this.listAlign);
18309         //this.list.endUpdate();
18310     },
18311
18312     // private
18313     onEmptyResults : function(){
18314         
18315         if(this.tickable && this.editable){
18316             this.hasFocus = false;
18317             this.restrictHeight();
18318             return;
18319         }
18320         
18321         this.collapse();
18322     },
18323
18324     /**
18325      * Returns true if the dropdown list is expanded, else false.
18326      */
18327     isExpanded : function(){
18328         return this.list.isVisible();
18329     },
18330
18331     /**
18332      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18333      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18334      * @param {String} value The data value of the item to select
18335      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18336      * selected item if it is not currently in view (defaults to true)
18337      * @return {Boolean} True if the value matched an item in the list, else false
18338      */
18339     selectByValue : function(v, scrollIntoView){
18340         if(v !== undefined && v !== null){
18341             var r = this.findRecord(this.valueField || this.displayField, v);
18342             if(r){
18343                 this.select(this.store.indexOf(r), scrollIntoView);
18344                 return true;
18345             }
18346         }
18347         return false;
18348     },
18349
18350     /**
18351      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18352      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18353      * @param {Number} index The zero-based index of the list item to select
18354      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18355      * selected item if it is not currently in view (defaults to true)
18356      */
18357     select : function(index, scrollIntoView){
18358         this.selectedIndex = index;
18359         this.view.select(index);
18360         if(scrollIntoView !== false){
18361             var el = this.view.getNode(index);
18362             /*
18363              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18364              */
18365             if(el){
18366                 this.list.scrollChildIntoView(el, false);
18367             }
18368         }
18369     },
18370
18371     // private
18372     selectNext : function(){
18373         var ct = this.store.getCount();
18374         if(ct > 0){
18375             if(this.selectedIndex == -1){
18376                 this.select(0);
18377             }else if(this.selectedIndex < ct-1){
18378                 this.select(this.selectedIndex+1);
18379             }
18380         }
18381     },
18382
18383     // private
18384     selectPrev : function(){
18385         var ct = this.store.getCount();
18386         if(ct > 0){
18387             if(this.selectedIndex == -1){
18388                 this.select(0);
18389             }else if(this.selectedIndex != 0){
18390                 this.select(this.selectedIndex-1);
18391             }
18392         }
18393     },
18394
18395     // private
18396     onKeyUp : function(e){
18397         if(this.editable !== false && !e.isSpecialKey()){
18398             this.lastKey = e.getKey();
18399             this.dqTask.delay(this.queryDelay);
18400         }
18401     },
18402
18403     // private
18404     validateBlur : function(){
18405         return !this.list || !this.list.isVisible();   
18406     },
18407
18408     // private
18409     initQuery : function(){
18410         
18411         var v = this.getRawValue();
18412         
18413         if(this.tickable && this.editable){
18414             v = this.tickableInputEl().getValue();
18415         }
18416         
18417         this.doQuery(v);
18418     },
18419
18420     // private
18421     doForce : function(){
18422         if(this.inputEl().dom.value.length > 0){
18423             this.inputEl().dom.value =
18424                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18425              
18426         }
18427     },
18428
18429     /**
18430      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18431      * query allowing the query action to be canceled if needed.
18432      * @param {String} query The SQL query to execute
18433      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18434      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18435      * saved in the current store (defaults to false)
18436      */
18437     doQuery : function(q, forceAll){
18438         
18439         if(q === undefined || q === null){
18440             q = '';
18441         }
18442         var qe = {
18443             query: q,
18444             forceAll: forceAll,
18445             combo: this,
18446             cancel:false
18447         };
18448         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18449             return false;
18450         }
18451         q = qe.query;
18452         
18453         forceAll = qe.forceAll;
18454         if(forceAll === true || (q.length >= this.minChars)){
18455             
18456             this.hasQuery = true;
18457             
18458             if(this.lastQuery != q || this.alwaysQuery){
18459                 this.lastQuery = q;
18460                 if(this.mode == 'local'){
18461                     this.selectedIndex = -1;
18462                     if(forceAll){
18463                         this.store.clearFilter();
18464                     }else{
18465                         
18466                         if(this.specialFilter){
18467                             this.fireEvent('specialfilter', this);
18468                             this.onLoad();
18469                             return;
18470                         }
18471                         
18472                         this.store.filter(this.displayField, q);
18473                     }
18474                     
18475                     this.store.fireEvent("datachanged", this.store);
18476                     
18477                     this.onLoad();
18478                     
18479                     
18480                 }else{
18481                     
18482                     this.store.baseParams[this.queryParam] = q;
18483                     
18484                     var options = {params : this.getParams(q)};
18485                     
18486                     if(this.loadNext){
18487                         options.add = true;
18488                         options.params.start = this.page * this.pageSize;
18489                     }
18490                     
18491                     this.store.load(options);
18492                     
18493                     /*
18494                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18495                      *  we should expand the list on onLoad
18496                      *  so command out it
18497                      */
18498 //                    this.expand();
18499                 }
18500             }else{
18501                 this.selectedIndex = -1;
18502                 this.onLoad();   
18503             }
18504         }
18505         
18506         this.loadNext = false;
18507     },
18508     
18509     // private
18510     getParams : function(q){
18511         var p = {};
18512         //p[this.queryParam] = q;
18513         
18514         if(this.pageSize){
18515             p.start = 0;
18516             p.limit = this.pageSize;
18517         }
18518         return p;
18519     },
18520
18521     /**
18522      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18523      */
18524     collapse : function(){
18525         if(!this.isExpanded()){
18526             return;
18527         }
18528         
18529         this.list.hide();
18530         
18531         this.hasFocus = false;
18532         
18533         if(this.tickable){
18534             this.okBtn.hide();
18535             this.cancelBtn.hide();
18536             this.trigger.show();
18537             
18538             if(this.editable){
18539                 this.tickableInputEl().dom.value = '';
18540                 this.tickableInputEl().blur();
18541             }
18542             
18543         }
18544         
18545         Roo.get(document).un('mousedown', this.collapseIf, this);
18546         Roo.get(document).un('mousewheel', this.collapseIf, this);
18547         if (!this.editable) {
18548             Roo.get(document).un('keydown', this.listKeyPress, this);
18549         }
18550         this.fireEvent('collapse', this);
18551         
18552         this.validate();
18553     },
18554
18555     // private
18556     collapseIf : function(e){
18557         var in_combo  = e.within(this.el);
18558         var in_list =  e.within(this.list);
18559         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18560         
18561         if (in_combo || in_list || is_list) {
18562             //e.stopPropagation();
18563             return;
18564         }
18565         
18566         if(this.tickable){
18567             this.onTickableFooterButtonClick(e, false, false);
18568         }
18569
18570         this.collapse();
18571         
18572     },
18573
18574     /**
18575      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18576      */
18577     expand : function(){
18578        
18579         if(this.isExpanded() || !this.hasFocus){
18580             return;
18581         }
18582         
18583         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18584         this.list.setWidth(lw);
18585         
18586         Roo.log('expand');
18587         
18588         this.list.show();
18589         
18590         this.restrictHeight();
18591         
18592         if(this.tickable){
18593             
18594             this.tickItems = Roo.apply([], this.item);
18595             
18596             this.okBtn.show();
18597             this.cancelBtn.show();
18598             this.trigger.hide();
18599             
18600             if(this.editable){
18601                 this.tickableInputEl().focus();
18602             }
18603             
18604         }
18605         
18606         Roo.get(document).on('mousedown', this.collapseIf, this);
18607         Roo.get(document).on('mousewheel', this.collapseIf, this);
18608         if (!this.editable) {
18609             Roo.get(document).on('keydown', this.listKeyPress, this);
18610         }
18611         
18612         this.fireEvent('expand', this);
18613     },
18614
18615     // private
18616     // Implements the default empty TriggerField.onTriggerClick function
18617     onTriggerClick : function(e)
18618     {
18619         Roo.log('trigger click');
18620         
18621         if(this.disabled || !this.triggerList){
18622             return;
18623         }
18624         
18625         this.page = 0;
18626         this.loadNext = false;
18627         
18628         if(this.isExpanded()){
18629             this.collapse();
18630             if (!this.blockFocus) {
18631                 this.inputEl().focus();
18632             }
18633             
18634         }else {
18635             this.hasFocus = true;
18636             if(this.triggerAction == 'all') {
18637                 this.doQuery(this.allQuery, true);
18638             } else {
18639                 this.doQuery(this.getRawValue());
18640             }
18641             if (!this.blockFocus) {
18642                 this.inputEl().focus();
18643             }
18644         }
18645     },
18646     
18647     onTickableTriggerClick : function(e)
18648     {
18649         if(this.disabled){
18650             return;
18651         }
18652         
18653         this.page = 0;
18654         this.loadNext = false;
18655         this.hasFocus = true;
18656         
18657         if(this.triggerAction == 'all') {
18658             this.doQuery(this.allQuery, true);
18659         } else {
18660             this.doQuery(this.getRawValue());
18661         }
18662     },
18663     
18664     onSearchFieldClick : function(e)
18665     {
18666         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18667             this.onTickableFooterButtonClick(e, false, false);
18668             return;
18669         }
18670         
18671         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18672             return;
18673         }
18674         
18675         this.page = 0;
18676         this.loadNext = false;
18677         this.hasFocus = true;
18678         
18679         if(this.triggerAction == 'all') {
18680             this.doQuery(this.allQuery, true);
18681         } else {
18682             this.doQuery(this.getRawValue());
18683         }
18684     },
18685     
18686     listKeyPress : function(e)
18687     {
18688         //Roo.log('listkeypress');
18689         // scroll to first matching element based on key pres..
18690         if (e.isSpecialKey()) {
18691             return false;
18692         }
18693         var k = String.fromCharCode(e.getKey()).toUpperCase();
18694         //Roo.log(k);
18695         var match  = false;
18696         var csel = this.view.getSelectedNodes();
18697         var cselitem = false;
18698         if (csel.length) {
18699             var ix = this.view.indexOf(csel[0]);
18700             cselitem  = this.store.getAt(ix);
18701             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18702                 cselitem = false;
18703             }
18704             
18705         }
18706         
18707         this.store.each(function(v) { 
18708             if (cselitem) {
18709                 // start at existing selection.
18710                 if (cselitem.id == v.id) {
18711                     cselitem = false;
18712                 }
18713                 return true;
18714             }
18715                 
18716             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18717                 match = this.store.indexOf(v);
18718                 return false;
18719             }
18720             return true;
18721         }, this);
18722         
18723         if (match === false) {
18724             return true; // no more action?
18725         }
18726         // scroll to?
18727         this.view.select(match);
18728         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18729         sn.scrollIntoView(sn.dom.parentNode, false);
18730     },
18731     
18732     onViewScroll : function(e, t){
18733         
18734         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){
18735             return;
18736         }
18737         
18738         this.hasQuery = true;
18739         
18740         this.loading = this.list.select('.loading', true).first();
18741         
18742         if(this.loading === null){
18743             this.list.createChild({
18744                 tag: 'div',
18745                 cls: 'loading roo-select2-more-results roo-select2-active',
18746                 html: 'Loading more results...'
18747             });
18748             
18749             this.loading = this.list.select('.loading', true).first();
18750             
18751             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18752             
18753             this.loading.hide();
18754         }
18755         
18756         this.loading.show();
18757         
18758         var _combo = this;
18759         
18760         this.page++;
18761         this.loadNext = true;
18762         
18763         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18764         
18765         return;
18766     },
18767     
18768     addItem : function(o)
18769     {   
18770         var dv = ''; // display value
18771         
18772         if (this.displayField) {
18773             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18774         } else {
18775             // this is an error condition!!!
18776             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18777         }
18778         
18779         if(!dv.length){
18780             return;
18781         }
18782         
18783         var choice = this.choices.createChild({
18784             tag: 'li',
18785             cls: 'roo-select2-search-choice',
18786             cn: [
18787                 {
18788                     tag: 'div',
18789                     html: dv
18790                 },
18791                 {
18792                     tag: 'a',
18793                     href: '#',
18794                     cls: 'roo-select2-search-choice-close fa fa-times',
18795                     tabindex: '-1'
18796                 }
18797             ]
18798             
18799         }, this.searchField);
18800         
18801         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18802         
18803         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18804         
18805         this.item.push(o);
18806         
18807         this.lastData = o;
18808         
18809         this.syncValue();
18810         
18811         this.inputEl().dom.value = '';
18812         
18813         this.validate();
18814     },
18815     
18816     onRemoveItem : function(e, _self, o)
18817     {
18818         e.preventDefault();
18819         
18820         this.lastItem = Roo.apply([], this.item);
18821         
18822         var index = this.item.indexOf(o.data) * 1;
18823         
18824         if( index < 0){
18825             Roo.log('not this item?!');
18826             return;
18827         }
18828         
18829         this.item.splice(index, 1);
18830         o.item.remove();
18831         
18832         this.syncValue();
18833         
18834         this.fireEvent('remove', this, e);
18835         
18836         this.validate();
18837         
18838     },
18839     
18840     syncValue : function()
18841     {
18842         if(!this.item.length){
18843             this.clearValue();
18844             return;
18845         }
18846             
18847         var value = [];
18848         var _this = this;
18849         Roo.each(this.item, function(i){
18850             if(_this.valueField){
18851                 value.push(i[_this.valueField]);
18852                 return;
18853             }
18854
18855             value.push(i);
18856         });
18857
18858         this.value = value.join(',');
18859
18860         if(this.hiddenField){
18861             this.hiddenField.dom.value = this.value;
18862         }
18863         
18864         this.store.fireEvent("datachanged", this.store);
18865         
18866         this.validate();
18867     },
18868     
18869     clearItem : function()
18870     {
18871         if(!this.multiple){
18872             return;
18873         }
18874         
18875         this.item = [];
18876         
18877         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18878            c.remove();
18879         });
18880         
18881         this.syncValue();
18882         
18883         this.validate();
18884         
18885         if(this.tickable && !Roo.isTouch){
18886             this.view.refresh();
18887         }
18888     },
18889     
18890     inputEl: function ()
18891     {
18892         if(Roo.isIOS && this.useNativeIOS){
18893             return this.el.select('select.roo-ios-select', true).first();
18894         }
18895         
18896         if(Roo.isTouch && this.mobileTouchView){
18897             return this.el.select('input.form-control',true).first();
18898         }
18899         
18900         if(this.tickable){
18901             return this.searchField;
18902         }
18903         
18904         return this.el.select('input.form-control',true).first();
18905     },
18906     
18907     onTickableFooterButtonClick : function(e, btn, el)
18908     {
18909         e.preventDefault();
18910         
18911         this.lastItem = Roo.apply([], this.item);
18912         
18913         if(btn && btn.name == 'cancel'){
18914             this.tickItems = Roo.apply([], this.item);
18915             this.collapse();
18916             return;
18917         }
18918         
18919         this.clearItem();
18920         
18921         var _this = this;
18922         
18923         Roo.each(this.tickItems, function(o){
18924             _this.addItem(o);
18925         });
18926         
18927         this.collapse();
18928         
18929     },
18930     
18931     validate : function()
18932     {
18933         if(this.getVisibilityEl().hasClass('hidden')){
18934             return true;
18935         }
18936         
18937         var v = this.getRawValue();
18938         
18939         if(this.multiple){
18940             v = this.getValue();
18941         }
18942         
18943         if(this.disabled || this.allowBlank || v.length){
18944             this.markValid();
18945             return true;
18946         }
18947         
18948         this.markInvalid();
18949         return false;
18950     },
18951     
18952     tickableInputEl : function()
18953     {
18954         if(!this.tickable || !this.editable){
18955             return this.inputEl();
18956         }
18957         
18958         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18959     },
18960     
18961     
18962     getAutoCreateTouchView : function()
18963     {
18964         var id = Roo.id();
18965         
18966         var cfg = {
18967             cls: 'form-group' //input-group
18968         };
18969         
18970         var input =  {
18971             tag: 'input',
18972             id : id,
18973             type : this.inputType,
18974             cls : 'form-control x-combo-noedit',
18975             autocomplete: 'new-password',
18976             placeholder : this.placeholder || '',
18977             readonly : true
18978         };
18979         
18980         if (this.name) {
18981             input.name = this.name;
18982         }
18983         
18984         if (this.size) {
18985             input.cls += ' input-' + this.size;
18986         }
18987         
18988         if (this.disabled) {
18989             input.disabled = true;
18990         }
18991         
18992         var inputblock = {
18993             cls : 'roo-combobox-wrap',
18994             cn : [
18995                 input
18996             ]
18997         };
18998         
18999         if(this.before){
19000             inputblock.cls += ' input-group';
19001             
19002             inputblock.cn.unshift({
19003                 tag :'span',
19004                 cls : 'input-group-addon input-group-prepend input-group-text',
19005                 html : this.before
19006             });
19007         }
19008         
19009         if(this.removable && !this.multiple){
19010             inputblock.cls += ' roo-removable';
19011             
19012             inputblock.cn.push({
19013                 tag: 'button',
19014                 html : 'x',
19015                 cls : 'roo-combo-removable-btn close'
19016             });
19017         }
19018
19019         if(this.hasFeedback && !this.allowBlank){
19020             
19021             inputblock.cls += ' has-feedback';
19022             
19023             inputblock.cn.push({
19024                 tag: 'span',
19025                 cls: 'glyphicon form-control-feedback'
19026             });
19027             
19028         }
19029         
19030         if (this.after) {
19031             
19032             inputblock.cls += (this.before) ? '' : ' input-group';
19033             
19034             inputblock.cn.push({
19035                 tag :'span',
19036                 cls : 'input-group-addon input-group-append input-group-text',
19037                 html : this.after
19038             });
19039         }
19040
19041         
19042         var ibwrap = inputblock;
19043         
19044         if(this.multiple){
19045             ibwrap = {
19046                 tag: 'ul',
19047                 cls: 'roo-select2-choices',
19048                 cn:[
19049                     {
19050                         tag: 'li',
19051                         cls: 'roo-select2-search-field',
19052                         cn: [
19053
19054                             inputblock
19055                         ]
19056                     }
19057                 ]
19058             };
19059         
19060             
19061         }
19062         
19063         var combobox = {
19064             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19065             cn: [
19066                 {
19067                     tag: 'input',
19068                     type : 'hidden',
19069                     cls: 'form-hidden-field'
19070                 },
19071                 ibwrap
19072             ]
19073         };
19074         
19075         if(!this.multiple && this.showToggleBtn){
19076             
19077             var caret = {
19078                 cls: 'caret'
19079             };
19080             
19081             if (this.caret != false) {
19082                 caret = {
19083                      tag: 'i',
19084                      cls: 'fa fa-' + this.caret
19085                 };
19086                 
19087             }
19088             
19089             combobox.cn.push({
19090                 tag :'span',
19091                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19092                 cn : [
19093                     Roo.bootstrap.version == 3 ? caret : '',
19094                     {
19095                         tag: 'span',
19096                         cls: 'combobox-clear',
19097                         cn  : [
19098                             {
19099                                 tag : 'i',
19100                                 cls: 'icon-remove'
19101                             }
19102                         ]
19103                     }
19104                 ]
19105
19106             })
19107         }
19108         
19109         if(this.multiple){
19110             combobox.cls += ' roo-select2-container-multi';
19111         }
19112         
19113         var required =  this.allowBlank ?  {
19114                     tag : 'i',
19115                     style: 'display: none'
19116                 } : {
19117                    tag : 'i',
19118                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19119                    tooltip : 'This field is required'
19120                 };
19121         
19122         var align = this.labelAlign || this.parentLabelAlign();
19123         
19124         if (align ==='left' && this.fieldLabel.length) {
19125
19126             cfg.cn = [
19127                 required,
19128                 {
19129                     tag: 'label',
19130                     cls : 'control-label col-form-label',
19131                     html : this.fieldLabel
19132
19133                 },
19134                 {
19135                     cls : 'roo-combobox-wrap ', 
19136                     cn: [
19137                         combobox
19138                     ]
19139                 }
19140             ];
19141             
19142             var labelCfg = cfg.cn[1];
19143             var contentCfg = cfg.cn[2];
19144             
19145
19146             if(this.indicatorpos == 'right'){
19147                 cfg.cn = [
19148                     {
19149                         tag: 'label',
19150                         'for' :  id,
19151                         cls : 'control-label col-form-label',
19152                         cn : [
19153                             {
19154                                 tag : 'span',
19155                                 html : this.fieldLabel
19156                             },
19157                             required
19158                         ]
19159                     },
19160                     {
19161                         cls : "roo-combobox-wrap ",
19162                         cn: [
19163                             combobox
19164                         ]
19165                     }
19166
19167                 ];
19168                 
19169                 labelCfg = cfg.cn[0];
19170                 contentCfg = cfg.cn[1];
19171             }
19172             
19173            
19174             
19175             if(this.labelWidth > 12){
19176                 labelCfg.style = "width: " + this.labelWidth + 'px';
19177             }
19178            
19179             if(this.labelWidth < 13 && this.labelmd == 0){
19180                 this.labelmd = this.labelWidth;
19181             }
19182             
19183             if(this.labellg > 0){
19184                 labelCfg.cls += ' col-lg-' + this.labellg;
19185                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19186             }
19187             
19188             if(this.labelmd > 0){
19189                 labelCfg.cls += ' col-md-' + this.labelmd;
19190                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19191             }
19192             
19193             if(this.labelsm > 0){
19194                 labelCfg.cls += ' col-sm-' + this.labelsm;
19195                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19196             }
19197             
19198             if(this.labelxs > 0){
19199                 labelCfg.cls += ' col-xs-' + this.labelxs;
19200                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19201             }
19202                 
19203                 
19204         } else if ( this.fieldLabel.length) {
19205             cfg.cn = [
19206                required,
19207                 {
19208                     tag: 'label',
19209                     cls : 'control-label',
19210                     html : this.fieldLabel
19211
19212                 },
19213                 {
19214                     cls : '', 
19215                     cn: [
19216                         combobox
19217                     ]
19218                 }
19219             ];
19220             
19221             if(this.indicatorpos == 'right'){
19222                 cfg.cn = [
19223                     {
19224                         tag: 'label',
19225                         cls : 'control-label',
19226                         html : this.fieldLabel,
19227                         cn : [
19228                             required
19229                         ]
19230                     },
19231                     {
19232                         cls : '', 
19233                         cn: [
19234                             combobox
19235                         ]
19236                     }
19237                 ];
19238             }
19239         } else {
19240             cfg.cn = combobox;    
19241         }
19242         
19243         
19244         var settings = this;
19245         
19246         ['xs','sm','md','lg'].map(function(size){
19247             if (settings[size]) {
19248                 cfg.cls += ' col-' + size + '-' + settings[size];
19249             }
19250         });
19251         
19252         return cfg;
19253     },
19254     
19255     initTouchView : function()
19256     {
19257         this.renderTouchView();
19258         
19259         this.touchViewEl.on('scroll', function(){
19260             this.el.dom.scrollTop = 0;
19261         }, this);
19262         
19263         this.originalValue = this.getValue();
19264         
19265         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19266         
19267         this.inputEl().on("click", this.showTouchView, this);
19268         if (this.triggerEl) {
19269             this.triggerEl.on("click", this.showTouchView, this);
19270         }
19271         
19272         
19273         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19274         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19275         
19276         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19277         
19278         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19279         this.store.on('load', this.onTouchViewLoad, this);
19280         this.store.on('loadexception', this.onTouchViewLoadException, this);
19281         
19282         if(this.hiddenName){
19283             
19284             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19285             
19286             this.hiddenField.dom.value =
19287                 this.hiddenValue !== undefined ? this.hiddenValue :
19288                 this.value !== undefined ? this.value : '';
19289         
19290             this.el.dom.removeAttribute('name');
19291             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19292         }
19293         
19294         if(this.multiple){
19295             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19296             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19297         }
19298         
19299         if(this.removable && !this.multiple){
19300             var close = this.closeTriggerEl();
19301             if(close){
19302                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19303                 close.on('click', this.removeBtnClick, this, close);
19304             }
19305         }
19306         /*
19307          * fix the bug in Safari iOS8
19308          */
19309         this.inputEl().on("focus", function(e){
19310             document.activeElement.blur();
19311         }, this);
19312         
19313         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19314         
19315         return;
19316         
19317         
19318     },
19319     
19320     renderTouchView : function()
19321     {
19322         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19323         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19324         
19325         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19326         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19327         
19328         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19329         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19330         this.touchViewBodyEl.setStyle('overflow', 'auto');
19331         
19332         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19333         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19334         
19335         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19336         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19337         
19338     },
19339     
19340     showTouchView : function()
19341     {
19342         if(this.disabled){
19343             return;
19344         }
19345         
19346         this.touchViewHeaderEl.hide();
19347
19348         if(this.modalTitle.length){
19349             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19350             this.touchViewHeaderEl.show();
19351         }
19352
19353         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19354         this.touchViewEl.show();
19355
19356         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19357         
19358         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19359         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19360
19361         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19362
19363         if(this.modalTitle.length){
19364             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19365         }
19366         
19367         this.touchViewBodyEl.setHeight(bodyHeight);
19368
19369         if(this.animate){
19370             var _this = this;
19371             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19372         }else{
19373             this.touchViewEl.addClass(['in','show']);
19374         }
19375         
19376         if(this._touchViewMask){
19377             Roo.get(document.body).addClass("x-body-masked");
19378             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19379             this._touchViewMask.setStyle('z-index', 10000);
19380             this._touchViewMask.addClass('show');
19381         }
19382         
19383         this.doTouchViewQuery();
19384         
19385     },
19386     
19387     hideTouchView : function()
19388     {
19389         this.touchViewEl.removeClass(['in','show']);
19390
19391         if(this.animate){
19392             var _this = this;
19393             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19394         }else{
19395             this.touchViewEl.setStyle('display', 'none');
19396         }
19397         
19398         if(this._touchViewMask){
19399             this._touchViewMask.removeClass('show');
19400             Roo.get(document.body).removeClass("x-body-masked");
19401         }
19402     },
19403     
19404     setTouchViewValue : function()
19405     {
19406         if(this.multiple){
19407             this.clearItem();
19408         
19409             var _this = this;
19410
19411             Roo.each(this.tickItems, function(o){
19412                 this.addItem(o);
19413             }, this);
19414         }
19415         
19416         this.hideTouchView();
19417     },
19418     
19419     doTouchViewQuery : function()
19420     {
19421         var qe = {
19422             query: '',
19423             forceAll: true,
19424             combo: this,
19425             cancel:false
19426         };
19427         
19428         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19429             return false;
19430         }
19431         
19432         if(!this.alwaysQuery || this.mode == 'local'){
19433             this.onTouchViewLoad();
19434             return;
19435         }
19436         
19437         this.store.load();
19438     },
19439     
19440     onTouchViewBeforeLoad : function(combo,opts)
19441     {
19442         return;
19443     },
19444
19445     // private
19446     onTouchViewLoad : function()
19447     {
19448         if(this.store.getCount() < 1){
19449             this.onTouchViewEmptyResults();
19450             return;
19451         }
19452         
19453         this.clearTouchView();
19454         
19455         var rawValue = this.getRawValue();
19456         
19457         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19458         
19459         this.tickItems = [];
19460         
19461         this.store.data.each(function(d, rowIndex){
19462             var row = this.touchViewListGroup.createChild(template);
19463             
19464             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19465                 row.addClass(d.data.cls);
19466             }
19467             
19468             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19469                 var cfg = {
19470                     data : d.data,
19471                     html : d.data[this.displayField]
19472                 };
19473                 
19474                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19475                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19476                 }
19477             }
19478             row.removeClass('selected');
19479             if(!this.multiple && this.valueField &&
19480                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19481             {
19482                 // radio buttons..
19483                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19484                 row.addClass('selected');
19485             }
19486             
19487             if(this.multiple && this.valueField &&
19488                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19489             {
19490                 
19491                 // checkboxes...
19492                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19493                 this.tickItems.push(d.data);
19494             }
19495             
19496             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19497             
19498         }, this);
19499         
19500         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19501         
19502         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19503
19504         if(this.modalTitle.length){
19505             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19506         }
19507
19508         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19509         
19510         if(this.mobile_restrict_height && listHeight < bodyHeight){
19511             this.touchViewBodyEl.setHeight(listHeight);
19512         }
19513         
19514         var _this = this;
19515         
19516         if(firstChecked && listHeight > bodyHeight){
19517             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19518         }
19519         
19520     },
19521     
19522     onTouchViewLoadException : function()
19523     {
19524         this.hideTouchView();
19525     },
19526     
19527     onTouchViewEmptyResults : function()
19528     {
19529         this.clearTouchView();
19530         
19531         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19532         
19533         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19534         
19535     },
19536     
19537     clearTouchView : function()
19538     {
19539         this.touchViewListGroup.dom.innerHTML = '';
19540     },
19541     
19542     onTouchViewClick : function(e, el, o)
19543     {
19544         e.preventDefault();
19545         
19546         var row = o.row;
19547         var rowIndex = o.rowIndex;
19548         
19549         var r = this.store.getAt(rowIndex);
19550         
19551         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19552             
19553             if(!this.multiple){
19554                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19555                     c.dom.removeAttribute('checked');
19556                 }, this);
19557
19558                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19559
19560                 this.setFromData(r.data);
19561
19562                 var close = this.closeTriggerEl();
19563
19564                 if(close){
19565                     close.show();
19566                 }
19567
19568                 this.hideTouchView();
19569
19570                 this.fireEvent('select', this, r, rowIndex);
19571
19572                 return;
19573             }
19574
19575             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19576                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19577                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19578                 return;
19579             }
19580
19581             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19582             this.addItem(r.data);
19583             this.tickItems.push(r.data);
19584         }
19585     },
19586     
19587     getAutoCreateNativeIOS : function()
19588     {
19589         var cfg = {
19590             cls: 'form-group' //input-group,
19591         };
19592         
19593         var combobox =  {
19594             tag: 'select',
19595             cls : 'roo-ios-select'
19596         };
19597         
19598         if (this.name) {
19599             combobox.name = this.name;
19600         }
19601         
19602         if (this.disabled) {
19603             combobox.disabled = true;
19604         }
19605         
19606         var settings = this;
19607         
19608         ['xs','sm','md','lg'].map(function(size){
19609             if (settings[size]) {
19610                 cfg.cls += ' col-' + size + '-' + settings[size];
19611             }
19612         });
19613         
19614         cfg.cn = combobox;
19615         
19616         return cfg;
19617         
19618     },
19619     
19620     initIOSView : function()
19621     {
19622         this.store.on('load', this.onIOSViewLoad, this);
19623         
19624         return;
19625     },
19626     
19627     onIOSViewLoad : function()
19628     {
19629         if(this.store.getCount() < 1){
19630             return;
19631         }
19632         
19633         this.clearIOSView();
19634         
19635         if(this.allowBlank) {
19636             
19637             var default_text = '-- SELECT --';
19638             
19639             if(this.placeholder.length){
19640                 default_text = this.placeholder;
19641             }
19642             
19643             if(this.emptyTitle.length){
19644                 default_text += ' - ' + this.emptyTitle + ' -';
19645             }
19646             
19647             var opt = this.inputEl().createChild({
19648                 tag: 'option',
19649                 value : 0,
19650                 html : default_text
19651             });
19652             
19653             var o = {};
19654             o[this.valueField] = 0;
19655             o[this.displayField] = default_text;
19656             
19657             this.ios_options.push({
19658                 data : o,
19659                 el : opt
19660             });
19661             
19662         }
19663         
19664         this.store.data.each(function(d, rowIndex){
19665             
19666             var html = '';
19667             
19668             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19669                 html = d.data[this.displayField];
19670             }
19671             
19672             var value = '';
19673             
19674             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19675                 value = d.data[this.valueField];
19676             }
19677             
19678             var option = {
19679                 tag: 'option',
19680                 value : value,
19681                 html : html
19682             };
19683             
19684             if(this.value == d.data[this.valueField]){
19685                 option['selected'] = true;
19686             }
19687             
19688             var opt = this.inputEl().createChild(option);
19689             
19690             this.ios_options.push({
19691                 data : d.data,
19692                 el : opt
19693             });
19694             
19695         }, this);
19696         
19697         this.inputEl().on('change', function(){
19698            this.fireEvent('select', this);
19699         }, this);
19700         
19701     },
19702     
19703     clearIOSView: function()
19704     {
19705         this.inputEl().dom.innerHTML = '';
19706         
19707         this.ios_options = [];
19708     },
19709     
19710     setIOSValue: function(v)
19711     {
19712         this.value = v;
19713         
19714         if(!this.ios_options){
19715             return;
19716         }
19717         
19718         Roo.each(this.ios_options, function(opts){
19719            
19720            opts.el.dom.removeAttribute('selected');
19721            
19722            if(opts.data[this.valueField] != v){
19723                return;
19724            }
19725            
19726            opts.el.dom.setAttribute('selected', true);
19727            
19728         }, this);
19729     }
19730
19731     /** 
19732     * @cfg {Boolean} grow 
19733     * @hide 
19734     */
19735     /** 
19736     * @cfg {Number} growMin 
19737     * @hide 
19738     */
19739     /** 
19740     * @cfg {Number} growMax 
19741     * @hide 
19742     */
19743     /**
19744      * @hide
19745      * @method autoSize
19746      */
19747 });
19748
19749 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19750     
19751     header : {
19752         tag: 'div',
19753         cls: 'modal-header',
19754         cn: [
19755             {
19756                 tag: 'h4',
19757                 cls: 'modal-title'
19758             }
19759         ]
19760     },
19761     
19762     body : {
19763         tag: 'div',
19764         cls: 'modal-body',
19765         cn: [
19766             {
19767                 tag: 'ul',
19768                 cls: 'list-group'
19769             }
19770         ]
19771     },
19772     
19773     listItemRadio : {
19774         tag: 'li',
19775         cls: 'list-group-item',
19776         cn: [
19777             {
19778                 tag: 'span',
19779                 cls: 'roo-combobox-list-group-item-value'
19780             },
19781             {
19782                 tag: 'div',
19783                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19784                 cn: [
19785                     {
19786                         tag: 'input',
19787                         type: 'radio'
19788                     },
19789                     {
19790                         tag: 'label'
19791                     }
19792                 ]
19793             }
19794         ]
19795     },
19796     
19797     listItemCheckbox : {
19798         tag: 'li',
19799         cls: 'list-group-item',
19800         cn: [
19801             {
19802                 tag: 'span',
19803                 cls: 'roo-combobox-list-group-item-value'
19804             },
19805             {
19806                 tag: 'div',
19807                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19808                 cn: [
19809                     {
19810                         tag: 'input',
19811                         type: 'checkbox'
19812                     },
19813                     {
19814                         tag: 'label'
19815                     }
19816                 ]
19817             }
19818         ]
19819     },
19820     
19821     emptyResult : {
19822         tag: 'div',
19823         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19824     },
19825     
19826     footer : {
19827         tag: 'div',
19828         cls: 'modal-footer',
19829         cn: [
19830             {
19831                 tag: 'div',
19832                 cls: 'row',
19833                 cn: [
19834                     {
19835                         tag: 'div',
19836                         cls: 'col-xs-6 text-left',
19837                         cn: {
19838                             tag: 'button',
19839                             cls: 'btn btn-danger roo-touch-view-cancel',
19840                             html: 'Cancel'
19841                         }
19842                     },
19843                     {
19844                         tag: 'div',
19845                         cls: 'col-xs-6 text-right',
19846                         cn: {
19847                             tag: 'button',
19848                             cls: 'btn btn-success roo-touch-view-ok',
19849                             html: 'OK'
19850                         }
19851                     }
19852                 ]
19853             }
19854         ]
19855         
19856     }
19857 });
19858
19859 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19860     
19861     touchViewTemplate : {
19862         tag: 'div',
19863         cls: 'modal fade roo-combobox-touch-view',
19864         cn: [
19865             {
19866                 tag: 'div',
19867                 cls: 'modal-dialog',
19868                 style : 'position:fixed', // we have to fix position....
19869                 cn: [
19870                     {
19871                         tag: 'div',
19872                         cls: 'modal-content',
19873                         cn: [
19874                             Roo.bootstrap.form.ComboBox.header,
19875                             Roo.bootstrap.form.ComboBox.body,
19876                             Roo.bootstrap.form.ComboBox.footer
19877                         ]
19878                     }
19879                 ]
19880             }
19881         ]
19882     }
19883 });/*
19884  * Based on:
19885  * Ext JS Library 1.1.1
19886  * Copyright(c) 2006-2007, Ext JS, LLC.
19887  *
19888  * Originally Released Under LGPL - original licence link has changed is not relivant.
19889  *
19890  * Fork - LGPL
19891  * <script type="text/javascript">
19892  */
19893
19894 /**
19895  * @class Roo.View
19896  * @extends Roo.util.Observable
19897  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19898  * This class also supports single and multi selection modes. <br>
19899  * Create a data model bound view:
19900  <pre><code>
19901  var store = new Roo.data.Store(...);
19902
19903  var view = new Roo.View({
19904     el : "my-element",
19905     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19906  
19907     singleSelect: true,
19908     selectedClass: "ydataview-selected",
19909     store: store
19910  });
19911
19912  // listen for node click?
19913  view.on("click", function(vw, index, node, e){
19914  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19915  });
19916
19917  // load XML data
19918  dataModel.load("foobar.xml");
19919  </code></pre>
19920  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19921  * <br><br>
19922  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19923  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19924  * 
19925  * Note: old style constructor is still suported (container, template, config)
19926  * 
19927  * @constructor
19928  * Create a new View
19929  * @param {Object} config The config object
19930  * 
19931  */
19932 Roo.View = function(config, depreciated_tpl, depreciated_config){
19933     
19934     this.parent = false;
19935     
19936     if (typeof(depreciated_tpl) == 'undefined') {
19937         // new way.. - universal constructor.
19938         Roo.apply(this, config);
19939         this.el  = Roo.get(this.el);
19940     } else {
19941         // old format..
19942         this.el  = Roo.get(config);
19943         this.tpl = depreciated_tpl;
19944         Roo.apply(this, depreciated_config);
19945     }
19946     this.wrapEl  = this.el.wrap().wrap();
19947     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19948     
19949     
19950     if(typeof(this.tpl) == "string"){
19951         this.tpl = new Roo.Template(this.tpl);
19952     } else {
19953         // support xtype ctors..
19954         this.tpl = new Roo.factory(this.tpl, Roo);
19955     }
19956     
19957     
19958     this.tpl.compile();
19959     
19960     /** @private */
19961     this.addEvents({
19962         /**
19963          * @event beforeclick
19964          * Fires before a click is processed. Returns false to cancel the default action.
19965          * @param {Roo.View} this
19966          * @param {Number} index The index of the target node
19967          * @param {HTMLElement} node The target node
19968          * @param {Roo.EventObject} e The raw event object
19969          */
19970             "beforeclick" : true,
19971         /**
19972          * @event click
19973          * Fires when a template node is clicked.
19974          * @param {Roo.View} this
19975          * @param {Number} index The index of the target node
19976          * @param {HTMLElement} node The target node
19977          * @param {Roo.EventObject} e The raw event object
19978          */
19979             "click" : true,
19980         /**
19981          * @event dblclick
19982          * Fires when a template node is double clicked.
19983          * @param {Roo.View} this
19984          * @param {Number} index The index of the target node
19985          * @param {HTMLElement} node The target node
19986          * @param {Roo.EventObject} e The raw event object
19987          */
19988             "dblclick" : true,
19989         /**
19990          * @event contextmenu
19991          * Fires when a template node is right clicked.
19992          * @param {Roo.View} this
19993          * @param {Number} index The index of the target node
19994          * @param {HTMLElement} node The target node
19995          * @param {Roo.EventObject} e The raw event object
19996          */
19997             "contextmenu" : true,
19998         /**
19999          * @event selectionchange
20000          * Fires when the selected nodes change.
20001          * @param {Roo.View} this
20002          * @param {Array} selections Array of the selected nodes
20003          */
20004             "selectionchange" : true,
20005     
20006         /**
20007          * @event beforeselect
20008          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20009          * @param {Roo.View} this
20010          * @param {HTMLElement} node The node to be selected
20011          * @param {Array} selections Array of currently selected nodes
20012          */
20013             "beforeselect" : true,
20014         /**
20015          * @event preparedata
20016          * Fires on every row to render, to allow you to change the data.
20017          * @param {Roo.View} this
20018          * @param {Object} data to be rendered (change this)
20019          */
20020           "preparedata" : true
20021           
20022           
20023         });
20024
20025
20026
20027     this.el.on({
20028         "click": this.onClick,
20029         "dblclick": this.onDblClick,
20030         "contextmenu": this.onContextMenu,
20031         scope:this
20032     });
20033
20034     this.selections = [];
20035     this.nodes = [];
20036     this.cmp = new Roo.CompositeElementLite([]);
20037     if(this.store){
20038         this.store = Roo.factory(this.store, Roo.data);
20039         this.setStore(this.store, true);
20040     }
20041     
20042     if ( this.footer && this.footer.xtype) {
20043            
20044          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20045         
20046         this.footer.dataSource = this.store;
20047         this.footer.container = fctr;
20048         this.footer = Roo.factory(this.footer, Roo);
20049         fctr.insertFirst(this.el);
20050         
20051         // this is a bit insane - as the paging toolbar seems to detach the el..
20052 //        dom.parentNode.parentNode.parentNode
20053          // they get detached?
20054     }
20055     
20056     
20057     Roo.View.superclass.constructor.call(this);
20058     
20059     
20060 };
20061
20062 Roo.extend(Roo.View, Roo.util.Observable, {
20063     
20064      /**
20065      * @cfg {Roo.data.Store} store Data store to load data from.
20066      */
20067     store : false,
20068     
20069     /**
20070      * @cfg {String|Roo.Element} el The container element.
20071      */
20072     el : '',
20073     
20074     /**
20075      * @cfg {String|Roo.Template} tpl The template used by this View 
20076      */
20077     tpl : false,
20078     /**
20079      * @cfg {String} dataName the named area of the template to use as the data area
20080      *                          Works with domtemplates roo-name="name"
20081      */
20082     dataName: false,
20083     /**
20084      * @cfg {String} selectedClass The css class to add to selected nodes
20085      */
20086     selectedClass : "x-view-selected",
20087      /**
20088      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20089      */
20090     emptyText : "",
20091     
20092     /**
20093      * @cfg {String} text to display on mask (default Loading)
20094      */
20095     mask : false,
20096     /**
20097      * @cfg {Boolean} multiSelect Allow multiple selection
20098      */
20099     multiSelect : false,
20100     /**
20101      * @cfg {Boolean} singleSelect Allow single selection
20102      */
20103     singleSelect:  false,
20104     
20105     /**
20106      * @cfg {Boolean} toggleSelect - selecting 
20107      */
20108     toggleSelect : false,
20109     
20110     /**
20111      * @cfg {Boolean} tickable - selecting 
20112      */
20113     tickable : false,
20114     
20115     /**
20116      * Returns the element this view is bound to.
20117      * @return {Roo.Element}
20118      */
20119     getEl : function(){
20120         return this.wrapEl;
20121     },
20122     
20123     
20124
20125     /**
20126      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20127      */
20128     refresh : function(){
20129         //Roo.log('refresh');
20130         var t = this.tpl;
20131         
20132         // if we are using something like 'domtemplate', then
20133         // the what gets used is:
20134         // t.applySubtemplate(NAME, data, wrapping data..)
20135         // the outer template then get' applied with
20136         //     the store 'extra data'
20137         // and the body get's added to the
20138         //      roo-name="data" node?
20139         //      <span class='roo-tpl-{name}'></span> ?????
20140         
20141         
20142         
20143         this.clearSelections();
20144         this.el.update("");
20145         var html = [];
20146         var records = this.store.getRange();
20147         if(records.length < 1) {
20148             
20149             // is this valid??  = should it render a template??
20150             
20151             this.el.update(this.emptyText);
20152             return;
20153         }
20154         var el = this.el;
20155         if (this.dataName) {
20156             this.el.update(t.apply(this.store.meta)); //????
20157             el = this.el.child('.roo-tpl-' + this.dataName);
20158         }
20159         
20160         for(var i = 0, len = records.length; i < len; i++){
20161             var data = this.prepareData(records[i].data, i, records[i]);
20162             this.fireEvent("preparedata", this, data, i, records[i]);
20163             
20164             var d = Roo.apply({}, data);
20165             
20166             if(this.tickable){
20167                 Roo.apply(d, {'roo-id' : Roo.id()});
20168                 
20169                 var _this = this;
20170             
20171                 Roo.each(this.parent.item, function(item){
20172                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20173                         return;
20174                     }
20175                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20176                 });
20177             }
20178             
20179             html[html.length] = Roo.util.Format.trim(
20180                 this.dataName ?
20181                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20182                     t.apply(d)
20183             );
20184         }
20185         
20186         
20187         
20188         el.update(html.join(""));
20189         this.nodes = el.dom.childNodes;
20190         this.updateIndexes(0);
20191     },
20192     
20193
20194     /**
20195      * Function to override to reformat the data that is sent to
20196      * the template for each node.
20197      * DEPRICATED - use the preparedata event handler.
20198      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20199      * a JSON object for an UpdateManager bound view).
20200      */
20201     prepareData : function(data, index, record)
20202     {
20203         this.fireEvent("preparedata", this, data, index, record);
20204         return data;
20205     },
20206
20207     onUpdate : function(ds, record){
20208         // Roo.log('on update');   
20209         this.clearSelections();
20210         var index = this.store.indexOf(record);
20211         var n = this.nodes[index];
20212         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20213         n.parentNode.removeChild(n);
20214         this.updateIndexes(index, index);
20215     },
20216
20217     
20218     
20219 // --------- FIXME     
20220     onAdd : function(ds, records, index)
20221     {
20222         //Roo.log(['on Add', ds, records, index] );        
20223         this.clearSelections();
20224         if(this.nodes.length == 0){
20225             this.refresh();
20226             return;
20227         }
20228         var n = this.nodes[index];
20229         for(var i = 0, len = records.length; i < len; i++){
20230             var d = this.prepareData(records[i].data, i, records[i]);
20231             if(n){
20232                 this.tpl.insertBefore(n, d);
20233             }else{
20234                 
20235                 this.tpl.append(this.el, d);
20236             }
20237         }
20238         this.updateIndexes(index);
20239     },
20240
20241     onRemove : function(ds, record, index){
20242        // Roo.log('onRemove');
20243         this.clearSelections();
20244         var el = this.dataName  ?
20245             this.el.child('.roo-tpl-' + this.dataName) :
20246             this.el; 
20247         
20248         el.dom.removeChild(this.nodes[index]);
20249         this.updateIndexes(index);
20250     },
20251
20252     /**
20253      * Refresh an individual node.
20254      * @param {Number} index
20255      */
20256     refreshNode : function(index){
20257         this.onUpdate(this.store, this.store.getAt(index));
20258     },
20259
20260     updateIndexes : function(startIndex, endIndex){
20261         var ns = this.nodes;
20262         startIndex = startIndex || 0;
20263         endIndex = endIndex || ns.length - 1;
20264         for(var i = startIndex; i <= endIndex; i++){
20265             ns[i].nodeIndex = i;
20266         }
20267     },
20268
20269     /**
20270      * Changes the data store this view uses and refresh the view.
20271      * @param {Store} store
20272      */
20273     setStore : function(store, initial){
20274         if(!initial && this.store){
20275             this.store.un("datachanged", this.refresh);
20276             this.store.un("add", this.onAdd);
20277             this.store.un("remove", this.onRemove);
20278             this.store.un("update", this.onUpdate);
20279             this.store.un("clear", this.refresh);
20280             this.store.un("beforeload", this.onBeforeLoad);
20281             this.store.un("load", this.onLoad);
20282             this.store.un("loadexception", this.onLoad);
20283         }
20284         if(store){
20285           
20286             store.on("datachanged", this.refresh, this);
20287             store.on("add", this.onAdd, this);
20288             store.on("remove", this.onRemove, this);
20289             store.on("update", this.onUpdate, this);
20290             store.on("clear", this.refresh, this);
20291             store.on("beforeload", this.onBeforeLoad, this);
20292             store.on("load", this.onLoad, this);
20293             store.on("loadexception", this.onLoad, this);
20294         }
20295         
20296         if(store){
20297             this.refresh();
20298         }
20299     },
20300     /**
20301      * onbeforeLoad - masks the loading area.
20302      *
20303      */
20304     onBeforeLoad : function(store,opts)
20305     {
20306          //Roo.log('onBeforeLoad');   
20307         if (!opts.add) {
20308             this.el.update("");
20309         }
20310         this.el.mask(this.mask ? this.mask : "Loading" ); 
20311     },
20312     onLoad : function ()
20313     {
20314         this.el.unmask();
20315     },
20316     
20317
20318     /**
20319      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20320      * @param {HTMLElement} node
20321      * @return {HTMLElement} The template node
20322      */
20323     findItemFromChild : function(node){
20324         var el = this.dataName  ?
20325             this.el.child('.roo-tpl-' + this.dataName,true) :
20326             this.el.dom; 
20327         
20328         if(!node || node.parentNode == el){
20329                     return node;
20330             }
20331             var p = node.parentNode;
20332             while(p && p != el){
20333             if(p.parentNode == el){
20334                 return p;
20335             }
20336             p = p.parentNode;
20337         }
20338             return null;
20339     },
20340
20341     /** @ignore */
20342     onClick : function(e){
20343         var item = this.findItemFromChild(e.getTarget());
20344         if(item){
20345             var index = this.indexOf(item);
20346             if(this.onItemClick(item, index, e) !== false){
20347                 this.fireEvent("click", this, index, item, e);
20348             }
20349         }else{
20350             this.clearSelections();
20351         }
20352     },
20353
20354     /** @ignore */
20355     onContextMenu : function(e){
20356         var item = this.findItemFromChild(e.getTarget());
20357         if(item){
20358             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20359         }
20360     },
20361
20362     /** @ignore */
20363     onDblClick : function(e){
20364         var item = this.findItemFromChild(e.getTarget());
20365         if(item){
20366             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20367         }
20368     },
20369
20370     onItemClick : function(item, index, e)
20371     {
20372         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20373             return false;
20374         }
20375         if (this.toggleSelect) {
20376             var m = this.isSelected(item) ? 'unselect' : 'select';
20377             //Roo.log(m);
20378             var _t = this;
20379             _t[m](item, true, false);
20380             return true;
20381         }
20382         if(this.multiSelect || this.singleSelect){
20383             if(this.multiSelect && e.shiftKey && this.lastSelection){
20384                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20385             }else{
20386                 this.select(item, this.multiSelect && e.ctrlKey);
20387                 this.lastSelection = item;
20388             }
20389             
20390             if(!this.tickable){
20391                 e.preventDefault();
20392             }
20393             
20394         }
20395         return true;
20396     },
20397
20398     /**
20399      * Get the number of selected nodes.
20400      * @return {Number}
20401      */
20402     getSelectionCount : function(){
20403         return this.selections.length;
20404     },
20405
20406     /**
20407      * Get the currently selected nodes.
20408      * @return {Array} An array of HTMLElements
20409      */
20410     getSelectedNodes : function(){
20411         return this.selections;
20412     },
20413
20414     /**
20415      * Get the indexes of the selected nodes.
20416      * @return {Array}
20417      */
20418     getSelectedIndexes : function(){
20419         var indexes = [], s = this.selections;
20420         for(var i = 0, len = s.length; i < len; i++){
20421             indexes.push(s[i].nodeIndex);
20422         }
20423         return indexes;
20424     },
20425
20426     /**
20427      * Clear all selections
20428      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20429      */
20430     clearSelections : function(suppressEvent){
20431         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20432             this.cmp.elements = this.selections;
20433             this.cmp.removeClass(this.selectedClass);
20434             this.selections = [];
20435             if(!suppressEvent){
20436                 this.fireEvent("selectionchange", this, this.selections);
20437             }
20438         }
20439     },
20440
20441     /**
20442      * Returns true if the passed node is selected
20443      * @param {HTMLElement/Number} node The node or node index
20444      * @return {Boolean}
20445      */
20446     isSelected : function(node){
20447         var s = this.selections;
20448         if(s.length < 1){
20449             return false;
20450         }
20451         node = this.getNode(node);
20452         return s.indexOf(node) !== -1;
20453     },
20454
20455     /**
20456      * Selects nodes.
20457      * @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
20458      * @param {Boolean} keepExisting (optional) true to keep existing selections
20459      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20460      */
20461     select : function(nodeInfo, keepExisting, suppressEvent){
20462         if(nodeInfo instanceof Array){
20463             if(!keepExisting){
20464                 this.clearSelections(true);
20465             }
20466             for(var i = 0, len = nodeInfo.length; i < len; i++){
20467                 this.select(nodeInfo[i], true, true);
20468             }
20469             return;
20470         } 
20471         var node = this.getNode(nodeInfo);
20472         if(!node || this.isSelected(node)){
20473             return; // already selected.
20474         }
20475         if(!keepExisting){
20476             this.clearSelections(true);
20477         }
20478         
20479         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20480             Roo.fly(node).addClass(this.selectedClass);
20481             this.selections.push(node);
20482             if(!suppressEvent){
20483                 this.fireEvent("selectionchange", this, this.selections);
20484             }
20485         }
20486         
20487         
20488     },
20489       /**
20490      * Unselects nodes.
20491      * @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
20492      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20493      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20494      */
20495     unselect : function(nodeInfo, keepExisting, suppressEvent)
20496     {
20497         if(nodeInfo instanceof Array){
20498             Roo.each(this.selections, function(s) {
20499                 this.unselect(s, nodeInfo);
20500             }, this);
20501             return;
20502         }
20503         var node = this.getNode(nodeInfo);
20504         if(!node || !this.isSelected(node)){
20505             //Roo.log("not selected");
20506             return; // not selected.
20507         }
20508         // fireevent???
20509         var ns = [];
20510         Roo.each(this.selections, function(s) {
20511             if (s == node ) {
20512                 Roo.fly(node).removeClass(this.selectedClass);
20513
20514                 return;
20515             }
20516             ns.push(s);
20517         },this);
20518         
20519         this.selections= ns;
20520         this.fireEvent("selectionchange", this, this.selections);
20521     },
20522
20523     /**
20524      * Gets a template node.
20525      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20526      * @return {HTMLElement} The node or null if it wasn't found
20527      */
20528     getNode : function(nodeInfo){
20529         if(typeof nodeInfo == "string"){
20530             return document.getElementById(nodeInfo);
20531         }else if(typeof nodeInfo == "number"){
20532             return this.nodes[nodeInfo];
20533         }
20534         return nodeInfo;
20535     },
20536
20537     /**
20538      * Gets a range template nodes.
20539      * @param {Number} startIndex
20540      * @param {Number} endIndex
20541      * @return {Array} An array of nodes
20542      */
20543     getNodes : function(start, end){
20544         var ns = this.nodes;
20545         start = start || 0;
20546         end = typeof end == "undefined" ? ns.length - 1 : end;
20547         var nodes = [];
20548         if(start <= end){
20549             for(var i = start; i <= end; i++){
20550                 nodes.push(ns[i]);
20551             }
20552         } else{
20553             for(var i = start; i >= end; i--){
20554                 nodes.push(ns[i]);
20555             }
20556         }
20557         return nodes;
20558     },
20559
20560     /**
20561      * Finds the index of the passed node
20562      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20563      * @return {Number} The index of the node or -1
20564      */
20565     indexOf : function(node){
20566         node = this.getNode(node);
20567         if(typeof node.nodeIndex == "number"){
20568             return node.nodeIndex;
20569         }
20570         var ns = this.nodes;
20571         for(var i = 0, len = ns.length; i < len; i++){
20572             if(ns[i] == node){
20573                 return i;
20574             }
20575         }
20576         return -1;
20577     }
20578 });
20579 /*
20580  * - LGPL
20581  *
20582  * based on jquery fullcalendar
20583  * 
20584  */
20585
20586 Roo.bootstrap = Roo.bootstrap || {};
20587 /**
20588  * @class Roo.bootstrap.Calendar
20589  * @extends Roo.bootstrap.Component
20590  * Bootstrap Calendar class
20591  * @cfg {Boolean} loadMask (true|false) default false
20592  * @cfg {Object} header generate the user specific header of the calendar, default false
20593
20594  * @constructor
20595  * Create a new Container
20596  * @param {Object} config The config object
20597  */
20598
20599
20600
20601 Roo.bootstrap.Calendar = function(config){
20602     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20603      this.addEvents({
20604         /**
20605              * @event select
20606              * Fires when a date is selected
20607              * @param {DatePicker} this
20608              * @param {Date} date The selected date
20609              */
20610         'select': true,
20611         /**
20612              * @event monthchange
20613              * Fires when the displayed month changes 
20614              * @param {DatePicker} this
20615              * @param {Date} date The selected month
20616              */
20617         'monthchange': true,
20618         /**
20619              * @event evententer
20620              * Fires when mouse over an event
20621              * @param {Calendar} this
20622              * @param {event} Event
20623              */
20624         'evententer': true,
20625         /**
20626              * @event eventleave
20627              * Fires when the mouse leaves an
20628              * @param {Calendar} this
20629              * @param {event}
20630              */
20631         'eventleave': true,
20632         /**
20633              * @event eventclick
20634              * Fires when the mouse click an
20635              * @param {Calendar} this
20636              * @param {event}
20637              */
20638         'eventclick': true
20639         
20640     });
20641
20642 };
20643
20644 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20645     
20646           /**
20647      * @cfg {Roo.data.Store} store
20648      * The data source for the calendar
20649      */
20650         store : false,
20651      /**
20652      * @cfg {Number} startDay
20653      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20654      */
20655     startDay : 0,
20656     
20657     loadMask : false,
20658     
20659     header : false,
20660       
20661     getAutoCreate : function(){
20662         
20663         
20664         var fc_button = function(name, corner, style, content ) {
20665             return Roo.apply({},{
20666                 tag : 'span',
20667                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20668                          (corner.length ?
20669                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20670                             ''
20671                         ),
20672                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20673                 unselectable: 'on'
20674             });
20675         };
20676         
20677         var header = {};
20678         
20679         if(!this.header){
20680             header = {
20681                 tag : 'table',
20682                 cls : 'fc-header',
20683                 style : 'width:100%',
20684                 cn : [
20685                     {
20686                         tag: 'tr',
20687                         cn : [
20688                             {
20689                                 tag : 'td',
20690                                 cls : 'fc-header-left',
20691                                 cn : [
20692                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20693                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20694                                     { tag: 'span', cls: 'fc-header-space' },
20695                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20696
20697
20698                                 ]
20699                             },
20700
20701                             {
20702                                 tag : 'td',
20703                                 cls : 'fc-header-center',
20704                                 cn : [
20705                                     {
20706                                         tag: 'span',
20707                                         cls: 'fc-header-title',
20708                                         cn : {
20709                                             tag: 'H2',
20710                                             html : 'month / year'
20711                                         }
20712                                     }
20713
20714                                 ]
20715                             },
20716                             {
20717                                 tag : 'td',
20718                                 cls : 'fc-header-right',
20719                                 cn : [
20720                               /*      fc_button('month', 'left', '', 'month' ),
20721                                     fc_button('week', '', '', 'week' ),
20722                                     fc_button('day', 'right', '', 'day' )
20723                                 */    
20724
20725                                 ]
20726                             }
20727
20728                         ]
20729                     }
20730                 ]
20731             };
20732         }
20733         
20734         header = this.header;
20735         
20736        
20737         var cal_heads = function() {
20738             var ret = [];
20739             // fixme - handle this.
20740             
20741             for (var i =0; i < Date.dayNames.length; i++) {
20742                 var d = Date.dayNames[i];
20743                 ret.push({
20744                     tag: 'th',
20745                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20746                     html : d.substring(0,3)
20747                 });
20748                 
20749             }
20750             ret[0].cls += ' fc-first';
20751             ret[6].cls += ' fc-last';
20752             return ret;
20753         };
20754         var cal_cell = function(n) {
20755             return  {
20756                 tag: 'td',
20757                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20758                 cn : [
20759                     {
20760                         cn : [
20761                             {
20762                                 cls: 'fc-day-number',
20763                                 html: 'D'
20764                             },
20765                             {
20766                                 cls: 'fc-day-content',
20767                              
20768                                 cn : [
20769                                      {
20770                                         style: 'position: relative;' // height: 17px;
20771                                     }
20772                                 ]
20773                             }
20774                             
20775                             
20776                         ]
20777                     }
20778                 ]
20779                 
20780             }
20781         };
20782         var cal_rows = function() {
20783             
20784             var ret = [];
20785             for (var r = 0; r < 6; r++) {
20786                 var row= {
20787                     tag : 'tr',
20788                     cls : 'fc-week',
20789                     cn : []
20790                 };
20791                 
20792                 for (var i =0; i < Date.dayNames.length; i++) {
20793                     var d = Date.dayNames[i];
20794                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20795
20796                 }
20797                 row.cn[0].cls+=' fc-first';
20798                 row.cn[0].cn[0].style = 'min-height:90px';
20799                 row.cn[6].cls+=' fc-last';
20800                 ret.push(row);
20801                 
20802             }
20803             ret[0].cls += ' fc-first';
20804             ret[4].cls += ' fc-prev-last';
20805             ret[5].cls += ' fc-last';
20806             return ret;
20807             
20808         };
20809         
20810         var cal_table = {
20811             tag: 'table',
20812             cls: 'fc-border-separate',
20813             style : 'width:100%',
20814             cellspacing  : 0,
20815             cn : [
20816                 { 
20817                     tag: 'thead',
20818                     cn : [
20819                         { 
20820                             tag: 'tr',
20821                             cls : 'fc-first fc-last',
20822                             cn : cal_heads()
20823                         }
20824                     ]
20825                 },
20826                 { 
20827                     tag: 'tbody',
20828                     cn : cal_rows()
20829                 }
20830                   
20831             ]
20832         };
20833          
20834          var cfg = {
20835             cls : 'fc fc-ltr',
20836             cn : [
20837                 header,
20838                 {
20839                     cls : 'fc-content',
20840                     style : "position: relative;",
20841                     cn : [
20842                         {
20843                             cls : 'fc-view fc-view-month fc-grid',
20844                             style : 'position: relative',
20845                             unselectable : 'on',
20846                             cn : [
20847                                 {
20848                                     cls : 'fc-event-container',
20849                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20850                                 },
20851                                 cal_table
20852                             ]
20853                         }
20854                     ]
20855     
20856                 }
20857            ] 
20858             
20859         };
20860         
20861          
20862         
20863         return cfg;
20864     },
20865     
20866     
20867     initEvents : function()
20868     {
20869         if(!this.store){
20870             throw "can not find store for calendar";
20871         }
20872         
20873         var mark = {
20874             tag: "div",
20875             cls:"x-dlg-mask",
20876             style: "text-align:center",
20877             cn: [
20878                 {
20879                     tag: "div",
20880                     style: "background-color:white;width:50%;margin:250 auto",
20881                     cn: [
20882                         {
20883                             tag: "img",
20884                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20885                         },
20886                         {
20887                             tag: "span",
20888                             html: "Loading"
20889                         }
20890                         
20891                     ]
20892                 }
20893             ]
20894         };
20895         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20896         
20897         var size = this.el.select('.fc-content', true).first().getSize();
20898         this.maskEl.setSize(size.width, size.height);
20899         this.maskEl.enableDisplayMode("block");
20900         if(!this.loadMask){
20901             this.maskEl.hide();
20902         }
20903         
20904         this.store = Roo.factory(this.store, Roo.data);
20905         this.store.on('load', this.onLoad, this);
20906         this.store.on('beforeload', this.onBeforeLoad, this);
20907         
20908         this.resize();
20909         
20910         this.cells = this.el.select('.fc-day',true);
20911         //Roo.log(this.cells);
20912         this.textNodes = this.el.query('.fc-day-number');
20913         this.cells.addClassOnOver('fc-state-hover');
20914         
20915         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20916         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20917         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20918         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20919         
20920         this.on('monthchange', this.onMonthChange, this);
20921         
20922         this.update(new Date().clearTime());
20923     },
20924     
20925     resize : function() {
20926         var sz  = this.el.getSize();
20927         
20928         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20929         this.el.select('.fc-day-content div',true).setHeight(34);
20930     },
20931     
20932     
20933     // private
20934     showPrevMonth : function(e){
20935         this.update(this.activeDate.add("mo", -1));
20936     },
20937     showToday : function(e){
20938         this.update(new Date().clearTime());
20939     },
20940     // private
20941     showNextMonth : function(e){
20942         this.update(this.activeDate.add("mo", 1));
20943     },
20944
20945     // private
20946     showPrevYear : function(){
20947         this.update(this.activeDate.add("y", -1));
20948     },
20949
20950     // private
20951     showNextYear : function(){
20952         this.update(this.activeDate.add("y", 1));
20953     },
20954
20955     
20956    // private
20957     update : function(date)
20958     {
20959         var vd = this.activeDate;
20960         this.activeDate = date;
20961 //        if(vd && this.el){
20962 //            var t = date.getTime();
20963 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20964 //                Roo.log('using add remove');
20965 //                
20966 //                this.fireEvent('monthchange', this, date);
20967 //                
20968 //                this.cells.removeClass("fc-state-highlight");
20969 //                this.cells.each(function(c){
20970 //                   if(c.dateValue == t){
20971 //                       c.addClass("fc-state-highlight");
20972 //                       setTimeout(function(){
20973 //                            try{c.dom.firstChild.focus();}catch(e){}
20974 //                       }, 50);
20975 //                       return false;
20976 //                   }
20977 //                   return true;
20978 //                });
20979 //                return;
20980 //            }
20981 //        }
20982         
20983         var days = date.getDaysInMonth();
20984         
20985         var firstOfMonth = date.getFirstDateOfMonth();
20986         var startingPos = firstOfMonth.getDay()-this.startDay;
20987         
20988         if(startingPos < this.startDay){
20989             startingPos += 7;
20990         }
20991         
20992         var pm = date.add(Date.MONTH, -1);
20993         var prevStart = pm.getDaysInMonth()-startingPos;
20994 //        
20995         this.cells = this.el.select('.fc-day',true);
20996         this.textNodes = this.el.query('.fc-day-number');
20997         this.cells.addClassOnOver('fc-state-hover');
20998         
20999         var cells = this.cells.elements;
21000         var textEls = this.textNodes;
21001         
21002         Roo.each(cells, function(cell){
21003             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21004         });
21005         
21006         days += startingPos;
21007
21008         // convert everything to numbers so it's fast
21009         var day = 86400000;
21010         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21011         //Roo.log(d);
21012         //Roo.log(pm);
21013         //Roo.log(prevStart);
21014         
21015         var today = new Date().clearTime().getTime();
21016         var sel = date.clearTime().getTime();
21017         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21018         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21019         var ddMatch = this.disabledDatesRE;
21020         var ddText = this.disabledDatesText;
21021         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21022         var ddaysText = this.disabledDaysText;
21023         var format = this.format;
21024         
21025         var setCellClass = function(cal, cell){
21026             cell.row = 0;
21027             cell.events = [];
21028             cell.more = [];
21029             //Roo.log('set Cell Class');
21030             cell.title = "";
21031             var t = d.getTime();
21032             
21033             //Roo.log(d);
21034             
21035             cell.dateValue = t;
21036             if(t == today){
21037                 cell.className += " fc-today";
21038                 cell.className += " fc-state-highlight";
21039                 cell.title = cal.todayText;
21040             }
21041             if(t == sel){
21042                 // disable highlight in other month..
21043                 //cell.className += " fc-state-highlight";
21044                 
21045             }
21046             // disabling
21047             if(t < min) {
21048                 cell.className = " fc-state-disabled";
21049                 cell.title = cal.minText;
21050                 return;
21051             }
21052             if(t > max) {
21053                 cell.className = " fc-state-disabled";
21054                 cell.title = cal.maxText;
21055                 return;
21056             }
21057             if(ddays){
21058                 if(ddays.indexOf(d.getDay()) != -1){
21059                     cell.title = ddaysText;
21060                     cell.className = " fc-state-disabled";
21061                 }
21062             }
21063             if(ddMatch && format){
21064                 var fvalue = d.dateFormat(format);
21065                 if(ddMatch.test(fvalue)){
21066                     cell.title = ddText.replace("%0", fvalue);
21067                     cell.className = " fc-state-disabled";
21068                 }
21069             }
21070             
21071             if (!cell.initialClassName) {
21072                 cell.initialClassName = cell.dom.className;
21073             }
21074             
21075             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21076         };
21077
21078         var i = 0;
21079         
21080         for(; i < startingPos; i++) {
21081             textEls[i].innerHTML = (++prevStart);
21082             d.setDate(d.getDate()+1);
21083             
21084             cells[i].className = "fc-past fc-other-month";
21085             setCellClass(this, cells[i]);
21086         }
21087         
21088         var intDay = 0;
21089         
21090         for(; i < days; i++){
21091             intDay = i - startingPos + 1;
21092             textEls[i].innerHTML = (intDay);
21093             d.setDate(d.getDate()+1);
21094             
21095             cells[i].className = ''; // "x-date-active";
21096             setCellClass(this, cells[i]);
21097         }
21098         var extraDays = 0;
21099         
21100         for(; i < 42; i++) {
21101             textEls[i].innerHTML = (++extraDays);
21102             d.setDate(d.getDate()+1);
21103             
21104             cells[i].className = "fc-future fc-other-month";
21105             setCellClass(this, cells[i]);
21106         }
21107         
21108         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21109         
21110         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21111         
21112         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21113         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21114         
21115         if(totalRows != 6){
21116             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21117             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21118         }
21119         
21120         this.fireEvent('monthchange', this, date);
21121         
21122         
21123         /*
21124         if(!this.internalRender){
21125             var main = this.el.dom.firstChild;
21126             var w = main.offsetWidth;
21127             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21128             Roo.fly(main).setWidth(w);
21129             this.internalRender = true;
21130             // opera does not respect the auto grow header center column
21131             // then, after it gets a width opera refuses to recalculate
21132             // without a second pass
21133             if(Roo.isOpera && !this.secondPass){
21134                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21135                 this.secondPass = true;
21136                 this.update.defer(10, this, [date]);
21137             }
21138         }
21139         */
21140         
21141     },
21142     
21143     findCell : function(dt) {
21144         dt = dt.clearTime().getTime();
21145         var ret = false;
21146         this.cells.each(function(c){
21147             //Roo.log("check " +c.dateValue + '?=' + dt);
21148             if(c.dateValue == dt){
21149                 ret = c;
21150                 return false;
21151             }
21152             return true;
21153         });
21154         
21155         return ret;
21156     },
21157     
21158     findCells : function(ev) {
21159         var s = ev.start.clone().clearTime().getTime();
21160        // Roo.log(s);
21161         var e= ev.end.clone().clearTime().getTime();
21162        // Roo.log(e);
21163         var ret = [];
21164         this.cells.each(function(c){
21165              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21166             
21167             if(c.dateValue > e){
21168                 return ;
21169             }
21170             if(c.dateValue < s){
21171                 return ;
21172             }
21173             ret.push(c);
21174         });
21175         
21176         return ret;    
21177     },
21178     
21179 //    findBestRow: function(cells)
21180 //    {
21181 //        var ret = 0;
21182 //        
21183 //        for (var i =0 ; i < cells.length;i++) {
21184 //            ret  = Math.max(cells[i].rows || 0,ret);
21185 //        }
21186 //        return ret;
21187 //        
21188 //    },
21189     
21190     
21191     addItem : function(ev)
21192     {
21193         // look for vertical location slot in
21194         var cells = this.findCells(ev);
21195         
21196 //        ev.row = this.findBestRow(cells);
21197         
21198         // work out the location.
21199         
21200         var crow = false;
21201         var rows = [];
21202         for(var i =0; i < cells.length; i++) {
21203             
21204             cells[i].row = cells[0].row;
21205             
21206             if(i == 0){
21207                 cells[i].row = cells[i].row + 1;
21208             }
21209             
21210             if (!crow) {
21211                 crow = {
21212                     start : cells[i],
21213                     end :  cells[i]
21214                 };
21215                 continue;
21216             }
21217             if (crow.start.getY() == cells[i].getY()) {
21218                 // on same row.
21219                 crow.end = cells[i];
21220                 continue;
21221             }
21222             // different row.
21223             rows.push(crow);
21224             crow = {
21225                 start: cells[i],
21226                 end : cells[i]
21227             };
21228             
21229         }
21230         
21231         rows.push(crow);
21232         ev.els = [];
21233         ev.rows = rows;
21234         ev.cells = cells;
21235         
21236         cells[0].events.push(ev);
21237         
21238         this.calevents.push(ev);
21239     },
21240     
21241     clearEvents: function() {
21242         
21243         if(!this.calevents){
21244             return;
21245         }
21246         
21247         Roo.each(this.cells.elements, function(c){
21248             c.row = 0;
21249             c.events = [];
21250             c.more = [];
21251         });
21252         
21253         Roo.each(this.calevents, function(e) {
21254             Roo.each(e.els, function(el) {
21255                 el.un('mouseenter' ,this.onEventEnter, this);
21256                 el.un('mouseleave' ,this.onEventLeave, this);
21257                 el.remove();
21258             },this);
21259         },this);
21260         
21261         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21262             e.remove();
21263         });
21264         
21265     },
21266     
21267     renderEvents: function()
21268     {   
21269         var _this = this;
21270         
21271         this.cells.each(function(c) {
21272             
21273             if(c.row < 5){
21274                 return;
21275             }
21276             
21277             var ev = c.events;
21278             
21279             var r = 4;
21280             if(c.row != c.events.length){
21281                 r = 4 - (4 - (c.row - c.events.length));
21282             }
21283             
21284             c.events = ev.slice(0, r);
21285             c.more = ev.slice(r);
21286             
21287             if(c.more.length && c.more.length == 1){
21288                 c.events.push(c.more.pop());
21289             }
21290             
21291             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21292             
21293         });
21294             
21295         this.cells.each(function(c) {
21296             
21297             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21298             
21299             
21300             for (var e = 0; e < c.events.length; e++){
21301                 var ev = c.events[e];
21302                 var rows = ev.rows;
21303                 
21304                 for(var i = 0; i < rows.length; i++) {
21305                 
21306                     // how many rows should it span..
21307
21308                     var  cfg = {
21309                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21310                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21311
21312                         unselectable : "on",
21313                         cn : [
21314                             {
21315                                 cls: 'fc-event-inner',
21316                                 cn : [
21317     //                                {
21318     //                                  tag:'span',
21319     //                                  cls: 'fc-event-time',
21320     //                                  html : cells.length > 1 ? '' : ev.time
21321     //                                },
21322                                     {
21323                                       tag:'span',
21324                                       cls: 'fc-event-title',
21325                                       html : String.format('{0}', ev.title)
21326                                     }
21327
21328
21329                                 ]
21330                             },
21331                             {
21332                                 cls: 'ui-resizable-handle ui-resizable-e',
21333                                 html : '&nbsp;&nbsp;&nbsp'
21334                             }
21335
21336                         ]
21337                     };
21338
21339                     if (i == 0) {
21340                         cfg.cls += ' fc-event-start';
21341                     }
21342                     if ((i+1) == rows.length) {
21343                         cfg.cls += ' fc-event-end';
21344                     }
21345
21346                     var ctr = _this.el.select('.fc-event-container',true).first();
21347                     var cg = ctr.createChild(cfg);
21348
21349                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21350                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21351
21352                     var r = (c.more.length) ? 1 : 0;
21353                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21354                     cg.setWidth(ebox.right - sbox.x -2);
21355
21356                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21357                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21358                     cg.on('click', _this.onEventClick, _this, ev);
21359
21360                     ev.els.push(cg);
21361                     
21362                 }
21363                 
21364             }
21365             
21366             
21367             if(c.more.length){
21368                 var  cfg = {
21369                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21370                     style : 'position: absolute',
21371                     unselectable : "on",
21372                     cn : [
21373                         {
21374                             cls: 'fc-event-inner',
21375                             cn : [
21376                                 {
21377                                   tag:'span',
21378                                   cls: 'fc-event-title',
21379                                   html : 'More'
21380                                 }
21381
21382
21383                             ]
21384                         },
21385                         {
21386                             cls: 'ui-resizable-handle ui-resizable-e',
21387                             html : '&nbsp;&nbsp;&nbsp'
21388                         }
21389
21390                     ]
21391                 };
21392
21393                 var ctr = _this.el.select('.fc-event-container',true).first();
21394                 var cg = ctr.createChild(cfg);
21395
21396                 var sbox = c.select('.fc-day-content',true).first().getBox();
21397                 var ebox = c.select('.fc-day-content',true).first().getBox();
21398                 //Roo.log(cg);
21399                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21400                 cg.setWidth(ebox.right - sbox.x -2);
21401
21402                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21403                 
21404             }
21405             
21406         });
21407         
21408         
21409         
21410     },
21411     
21412     onEventEnter: function (e, el,event,d) {
21413         this.fireEvent('evententer', this, el, event);
21414     },
21415     
21416     onEventLeave: function (e, el,event,d) {
21417         this.fireEvent('eventleave', this, el, event);
21418     },
21419     
21420     onEventClick: function (e, el,event,d) {
21421         this.fireEvent('eventclick', this, el, event);
21422     },
21423     
21424     onMonthChange: function () {
21425         this.store.load();
21426     },
21427     
21428     onMoreEventClick: function(e, el, more)
21429     {
21430         var _this = this;
21431         
21432         this.calpopover.placement = 'right';
21433         this.calpopover.setTitle('More');
21434         
21435         this.calpopover.setContent('');
21436         
21437         var ctr = this.calpopover.el.select('.popover-content', true).first();
21438         
21439         Roo.each(more, function(m){
21440             var cfg = {
21441                 cls : 'fc-event-hori fc-event-draggable',
21442                 html : m.title
21443             };
21444             var cg = ctr.createChild(cfg);
21445             
21446             cg.on('click', _this.onEventClick, _this, m);
21447         });
21448         
21449         this.calpopover.show(el);
21450         
21451         
21452     },
21453     
21454     onLoad: function () 
21455     {   
21456         this.calevents = [];
21457         var cal = this;
21458         
21459         if(this.store.getCount() > 0){
21460             this.store.data.each(function(d){
21461                cal.addItem({
21462                     id : d.data.id,
21463                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21464                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21465                     time : d.data.start_time,
21466                     title : d.data.title,
21467                     description : d.data.description,
21468                     venue : d.data.venue
21469                 });
21470             });
21471         }
21472         
21473         this.renderEvents();
21474         
21475         if(this.calevents.length && this.loadMask){
21476             this.maskEl.hide();
21477         }
21478     },
21479     
21480     onBeforeLoad: function()
21481     {
21482         this.clearEvents();
21483         if(this.loadMask){
21484             this.maskEl.show();
21485         }
21486     }
21487 });
21488
21489  
21490  /*
21491  * - LGPL
21492  *
21493  * element
21494  * 
21495  */
21496
21497 /**
21498  * @class Roo.bootstrap.Popover
21499  * @extends Roo.bootstrap.Component
21500  * @parent none builder
21501  * @children Roo.bootstrap.Component
21502  * Bootstrap Popover class
21503  * @cfg {String} html contents of the popover   (or false to use children..)
21504  * @cfg {String} title of popover (or false to hide)
21505  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21506  * @cfg {String} trigger click || hover (or false to trigger manually)
21507  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21508  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21509  *      - if false and it has a 'parent' then it will be automatically added to that element
21510  *      - if string - Roo.get  will be called 
21511  * @cfg {Number} delay - delay before showing
21512  
21513  * @constructor
21514  * Create a new Popover
21515  * @param {Object} config The config object
21516  */
21517
21518 Roo.bootstrap.Popover = function(config){
21519     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21520     
21521     this.addEvents({
21522         // raw events
21523          /**
21524          * @event show
21525          * After the popover show
21526          * 
21527          * @param {Roo.bootstrap.Popover} this
21528          */
21529         "show" : true,
21530         /**
21531          * @event hide
21532          * After the popover hide
21533          * 
21534          * @param {Roo.bootstrap.Popover} this
21535          */
21536         "hide" : true
21537     });
21538 };
21539
21540 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21541     
21542     title: false,
21543     html: false,
21544     
21545     placement : 'right',
21546     trigger : 'hover', // hover
21547     modal : false,
21548     delay : 0,
21549     
21550     over: false,
21551     
21552     can_build_overlaid : false,
21553     
21554     maskEl : false, // the mask element
21555     headerEl : false,
21556     contentEl : false,
21557     alignEl : false, // when show is called with an element - this get's stored.
21558     
21559     getChildContainer : function()
21560     {
21561         return this.contentEl;
21562         
21563     },
21564     getPopoverHeader : function()
21565     {
21566         this.title = true; // flag not to hide it..
21567         this.headerEl.addClass('p-0');
21568         return this.headerEl
21569     },
21570     
21571     
21572     getAutoCreate : function(){
21573          
21574         var cfg = {
21575            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21576            style: 'display:block',
21577            cn : [
21578                 {
21579                     cls : 'arrow'
21580                 },
21581                 {
21582                     cls : 'popover-inner ',
21583                     cn : [
21584                         {
21585                             tag: 'h3',
21586                             cls: 'popover-title popover-header',
21587                             html : this.title === false ? '' : this.title
21588                         },
21589                         {
21590                             cls : 'popover-content popover-body '  + (this.cls || ''),
21591                             html : this.html || ''
21592                         }
21593                     ]
21594                     
21595                 }
21596            ]
21597         };
21598         
21599         return cfg;
21600     },
21601     /**
21602      * @param {string} the title
21603      */
21604     setTitle: function(str)
21605     {
21606         this.title = str;
21607         if (this.el) {
21608             this.headerEl.dom.innerHTML = str;
21609         }
21610         
21611     },
21612     /**
21613      * @param {string} the body content
21614      */
21615     setContent: function(str)
21616     {
21617         this.html = str;
21618         if (this.contentEl) {
21619             this.contentEl.dom.innerHTML = str;
21620         }
21621         
21622     },
21623     // as it get's added to the bottom of the page.
21624     onRender : function(ct, position)
21625     {
21626         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21627         
21628         
21629         
21630         if(!this.el){
21631             var cfg = Roo.apply({},  this.getAutoCreate());
21632             cfg.id = Roo.id();
21633             
21634             if (this.cls) {
21635                 cfg.cls += ' ' + this.cls;
21636             }
21637             if (this.style) {
21638                 cfg.style = this.style;
21639             }
21640             //Roo.log("adding to ");
21641             this.el = Roo.get(document.body).createChild(cfg, position);
21642 //            Roo.log(this.el);
21643         }
21644         
21645         this.contentEl = this.el.select('.popover-content',true).first();
21646         this.headerEl =  this.el.select('.popover-title',true).first();
21647         
21648         var nitems = [];
21649         if(typeof(this.items) != 'undefined'){
21650             var items = this.items;
21651             delete this.items;
21652
21653             for(var i =0;i < items.length;i++) {
21654                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21655             }
21656         }
21657
21658         this.items = nitems;
21659         
21660         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21661         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21662         
21663         
21664         
21665         this.initEvents();
21666     },
21667     
21668     resizeMask : function()
21669     {
21670         this.maskEl.setSize(
21671             Roo.lib.Dom.getViewWidth(true),
21672             Roo.lib.Dom.getViewHeight(true)
21673         );
21674     },
21675     
21676     initEvents : function()
21677     {
21678         
21679         if (!this.modal) { 
21680             Roo.bootstrap.Popover.register(this);
21681         }
21682          
21683         this.arrowEl = this.el.select('.arrow',true).first();
21684         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21685         this.el.enableDisplayMode('block');
21686         this.el.hide();
21687  
21688         
21689         if (this.over === false && !this.parent()) {
21690             return; 
21691         }
21692         if (this.triggers === false) {
21693             return;
21694         }
21695          
21696         // support parent
21697         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21698         var triggers = this.trigger ? this.trigger.split(' ') : [];
21699         Roo.each(triggers, function(trigger) {
21700         
21701             if (trigger == 'click') {
21702                 on_el.on('click', this.toggle, this);
21703             } else if (trigger != 'manual') {
21704                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21705                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21706       
21707                 on_el.on(eventIn  ,this.enter, this);
21708                 on_el.on(eventOut, this.leave, this);
21709             }
21710         }, this);
21711     },
21712     
21713     
21714     // private
21715     timeout : null,
21716     hoverState : null,
21717     
21718     toggle : function () {
21719         this.hoverState == 'in' ? this.leave() : this.enter();
21720     },
21721     
21722     enter : function () {
21723         
21724         clearTimeout(this.timeout);
21725     
21726         this.hoverState = 'in';
21727     
21728         if (!this.delay || !this.delay.show) {
21729             this.show();
21730             return;
21731         }
21732         var _t = this;
21733         this.timeout = setTimeout(function () {
21734             if (_t.hoverState == 'in') {
21735                 _t.show();
21736             }
21737         }, this.delay.show)
21738     },
21739     
21740     leave : function() {
21741         clearTimeout(this.timeout);
21742     
21743         this.hoverState = 'out';
21744     
21745         if (!this.delay || !this.delay.hide) {
21746             this.hide();
21747             return;
21748         }
21749         var _t = this;
21750         this.timeout = setTimeout(function () {
21751             if (_t.hoverState == 'out') {
21752                 _t.hide();
21753             }
21754         }, this.delay.hide)
21755     },
21756     
21757     /**
21758      * update the position of the dialog
21759      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21760      * 
21761      *
21762      */
21763     
21764     doAlign : function()
21765     {
21766         
21767         if (this.alignEl) {
21768             this.updatePosition(this.placement, true);
21769              
21770         } else {
21771             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21772             var es = this.el.getSize();
21773             var x = Roo.lib.Dom.getViewWidth()/2;
21774             var y = Roo.lib.Dom.getViewHeight()/2;
21775             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21776             
21777         }
21778
21779          
21780          
21781         
21782         
21783     },
21784     
21785     /**
21786      * Show the popover
21787      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21788      * @param {string} (left|right|top|bottom) position
21789      */
21790     show : function (on_el, placement)
21791     {
21792         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21793         on_el = on_el || false; // default to false
21794          
21795         if (!on_el) {
21796             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21797                 on_el = this.parent().el;
21798             } else if (this.over) {
21799                 on_el = Roo.get(this.over);
21800             }
21801             
21802         }
21803         
21804         this.alignEl = Roo.get( on_el );
21805
21806         if (!this.el) {
21807             this.render(document.body);
21808         }
21809         
21810         
21811          
21812         
21813         if (this.title === false) {
21814             this.headerEl.hide();
21815         }
21816         
21817        
21818         this.el.show();
21819         this.el.dom.style.display = 'block';
21820          
21821         this.doAlign();
21822         
21823         //var arrow = this.el.select('.arrow',true).first();
21824         //arrow.set(align[2], 
21825         
21826         this.el.addClass('in');
21827         
21828          
21829         
21830         this.hoverState = 'in';
21831         
21832         if (this.modal) {
21833             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21834             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21835             this.maskEl.dom.style.display = 'block';
21836             this.maskEl.addClass('show');
21837         }
21838         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21839  
21840         this.fireEvent('show', this);
21841         
21842     },
21843     /**
21844      * fire this manually after loading a grid in the table for example
21845      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21846      * @param {Boolean} try and move it if we cant get right position.
21847      */
21848     updatePosition : function(placement, try_move)
21849     {
21850         // allow for calling with no parameters
21851         placement = placement   ? placement :  this.placement;
21852         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21853         
21854         this.el.removeClass([
21855             'fade','top','bottom', 'left', 'right','in',
21856             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21857         ]);
21858         this.el.addClass(placement + ' bs-popover-' + placement);
21859         
21860         if (!this.alignEl ) {
21861             return false;
21862         }
21863         
21864         switch (placement) {
21865             case 'right':
21866                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21867                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21868                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21869                     //normal display... or moved up/down.
21870                     this.el.setXY(offset);
21871                     var xy = this.alignEl.getAnchorXY('tr', false);
21872                     xy[0]+=2;xy[1]+=5;
21873                     this.arrowEl.setXY(xy);
21874                     return true;
21875                 }
21876                 // continue through...
21877                 return this.updatePosition('left', false);
21878                 
21879             
21880             case 'left':
21881                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21882                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21883                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21884                     //normal display... or moved up/down.
21885                     this.el.setXY(offset);
21886                     var xy = this.alignEl.getAnchorXY('tl', false);
21887                     xy[0]-=10;xy[1]+=5; // << fix me
21888                     this.arrowEl.setXY(xy);
21889                     return true;
21890                 }
21891                 // call self...
21892                 return this.updatePosition('right', false);
21893             
21894             case 'top':
21895                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21896                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21897                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21898                     //normal display... or moved up/down.
21899                     this.el.setXY(offset);
21900                     var xy = this.alignEl.getAnchorXY('t', false);
21901                     xy[1]-=10; // << fix me
21902                     this.arrowEl.setXY(xy);
21903                     return true;
21904                 }
21905                 // fall through
21906                return this.updatePosition('bottom', false);
21907             
21908             case 'bottom':
21909                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21910                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21911                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21912                     //normal display... or moved up/down.
21913                     this.el.setXY(offset);
21914                     var xy = this.alignEl.getAnchorXY('b', false);
21915                      xy[1]+=2; // << fix me
21916                     this.arrowEl.setXY(xy);
21917                     return true;
21918                 }
21919                 // fall through
21920                 return this.updatePosition('top', false);
21921                 
21922             
21923         }
21924         
21925         
21926         return false;
21927     },
21928     
21929     hide : function()
21930     {
21931         this.el.setXY([0,0]);
21932         this.el.removeClass('in');
21933         this.el.hide();
21934         this.hoverState = null;
21935         this.maskEl.hide(); // always..
21936         this.fireEvent('hide', this);
21937     }
21938     
21939 });
21940
21941
21942 Roo.apply(Roo.bootstrap.Popover, {
21943
21944     alignment : {
21945         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21946         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21947         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21948         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21949     },
21950     
21951     zIndex : 20001,
21952
21953     clickHander : false,
21954     
21955     
21956
21957     onMouseDown : function(e)
21958     {
21959         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21960             /// what is nothing is showing..
21961             this.hideAll();
21962         }
21963          
21964     },
21965     
21966     
21967     popups : [],
21968     
21969     register : function(popup)
21970     {
21971         if (!Roo.bootstrap.Popover.clickHandler) {
21972             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21973         }
21974         // hide other popups.
21975         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21976         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21977         this.hideAll(); //<< why?
21978         //this.popups.push(popup);
21979     },
21980     hideAll : function()
21981     {
21982         this.popups.forEach(function(p) {
21983             p.hide();
21984         });
21985     },
21986     onShow : function() {
21987         Roo.bootstrap.Popover.popups.push(this);
21988     },
21989     onHide : function() {
21990         Roo.bootstrap.Popover.popups.remove(this);
21991     } 
21992
21993 });
21994 /**
21995  * @class Roo.bootstrap.PopoverNav
21996  * @extends Roo.bootstrap.nav.Simplebar
21997  * @parent Roo.bootstrap.Popover
21998  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
21999  * @licence LGPL
22000  * Bootstrap Popover header navigation class
22001  * FIXME? should this go under nav?
22002  *
22003  * 
22004  * @constructor
22005  * Create a new Popover Header Navigation 
22006  * @param {Object} config The config object
22007  */
22008
22009 Roo.bootstrap.PopoverNav = function(config){
22010     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22011 };
22012
22013 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22014     
22015     
22016     container_method : 'getPopoverHeader' 
22017     
22018      
22019     
22020     
22021    
22022 });
22023
22024  
22025
22026  /*
22027  * - LGPL
22028  *
22029  * Progress
22030  * 
22031  */
22032
22033 /**
22034  * @class Roo.bootstrap.Progress
22035  * @extends Roo.bootstrap.Component
22036  * @children Roo.bootstrap.ProgressBar
22037  * Bootstrap Progress class
22038  * @cfg {Boolean} striped striped of the progress bar
22039  * @cfg {Boolean} active animated of the progress bar
22040  * 
22041  * 
22042  * @constructor
22043  * Create a new Progress
22044  * @param {Object} config The config object
22045  */
22046
22047 Roo.bootstrap.Progress = function(config){
22048     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22049 };
22050
22051 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22052     
22053     striped : false,
22054     active: false,
22055     
22056     getAutoCreate : function(){
22057         var cfg = {
22058             tag: 'div',
22059             cls: 'progress'
22060         };
22061         
22062         
22063         if(this.striped){
22064             cfg.cls += ' progress-striped';
22065         }
22066       
22067         if(this.active){
22068             cfg.cls += ' active';
22069         }
22070         
22071         
22072         return cfg;
22073     }
22074    
22075 });
22076
22077  
22078
22079  /*
22080  * - LGPL
22081  *
22082  * ProgressBar
22083  * 
22084  */
22085
22086 /**
22087  * @class Roo.bootstrap.ProgressBar
22088  * @extends Roo.bootstrap.Component
22089  * Bootstrap ProgressBar class
22090  * @cfg {Number} aria_valuenow aria-value now
22091  * @cfg {Number} aria_valuemin aria-value min
22092  * @cfg {Number} aria_valuemax aria-value max
22093  * @cfg {String} label label for the progress bar
22094  * @cfg {String} panel (success | info | warning | danger )
22095  * @cfg {String} role role of the progress bar
22096  * @cfg {String} sr_only text
22097  * 
22098  * 
22099  * @constructor
22100  * Create a new ProgressBar
22101  * @param {Object} config The config object
22102  */
22103
22104 Roo.bootstrap.ProgressBar = function(config){
22105     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22106 };
22107
22108 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22109     
22110     aria_valuenow : 0,
22111     aria_valuemin : 0,
22112     aria_valuemax : 100,
22113     label : false,
22114     panel : false,
22115     role : false,
22116     sr_only: false,
22117     
22118     getAutoCreate : function()
22119     {
22120         
22121         var cfg = {
22122             tag: 'div',
22123             cls: 'progress-bar',
22124             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22125         };
22126         
22127         if(this.sr_only){
22128             cfg.cn = {
22129                 tag: 'span',
22130                 cls: 'sr-only',
22131                 html: this.sr_only
22132             }
22133         }
22134         
22135         if(this.role){
22136             cfg.role = this.role;
22137         }
22138         
22139         if(this.aria_valuenow){
22140             cfg['aria-valuenow'] = this.aria_valuenow;
22141         }
22142         
22143         if(this.aria_valuemin){
22144             cfg['aria-valuemin'] = this.aria_valuemin;
22145         }
22146         
22147         if(this.aria_valuemax){
22148             cfg['aria-valuemax'] = this.aria_valuemax;
22149         }
22150         
22151         if(this.label && !this.sr_only){
22152             cfg.html = this.label;
22153         }
22154         
22155         if(this.panel){
22156             cfg.cls += ' progress-bar-' + this.panel;
22157         }
22158         
22159         return cfg;
22160     },
22161     
22162     update : function(aria_valuenow)
22163     {
22164         this.aria_valuenow = aria_valuenow;
22165         
22166         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22167     }
22168    
22169 });
22170
22171  
22172
22173  /**
22174  * @class Roo.bootstrap.TabGroup
22175  * @extends Roo.bootstrap.Column
22176  * @children Roo.bootstrap.TabPanel
22177  * Bootstrap Column class
22178  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22179  * @cfg {Boolean} carousel true to make the group behave like a carousel
22180  * @cfg {Boolean} bullets show bullets for the panels
22181  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22182  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22183  * @cfg {Boolean} showarrow (true|false) show arrow default true
22184  * 
22185  * @constructor
22186  * Create a new TabGroup
22187  * @param {Object} config The config object
22188  */
22189
22190 Roo.bootstrap.TabGroup = function(config){
22191     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22192     if (!this.navId) {
22193         this.navId = Roo.id();
22194     }
22195     this.tabs = [];
22196     Roo.bootstrap.TabGroup.register(this);
22197     
22198 };
22199
22200 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22201     
22202     carousel : false,
22203     transition : false,
22204     bullets : 0,
22205     timer : 0,
22206     autoslide : false,
22207     slideFn : false,
22208     slideOnTouch : false,
22209     showarrow : true,
22210     
22211     getAutoCreate : function()
22212     {
22213         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22214         
22215         cfg.cls += ' tab-content';
22216         
22217         if (this.carousel) {
22218             cfg.cls += ' carousel slide';
22219             
22220             cfg.cn = [{
22221                cls : 'carousel-inner',
22222                cn : []
22223             }];
22224         
22225             if(this.bullets  && !Roo.isTouch){
22226                 
22227                 var bullets = {
22228                     cls : 'carousel-bullets',
22229                     cn : []
22230                 };
22231                
22232                 if(this.bullets_cls){
22233                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22234                 }
22235                 
22236                 bullets.cn.push({
22237                     cls : 'clear'
22238                 });
22239                 
22240                 cfg.cn[0].cn.push(bullets);
22241             }
22242             
22243             if(this.showarrow){
22244                 cfg.cn[0].cn.push({
22245                     tag : 'div',
22246                     class : 'carousel-arrow',
22247                     cn : [
22248                         {
22249                             tag : 'div',
22250                             class : 'carousel-prev',
22251                             cn : [
22252                                 {
22253                                     tag : 'i',
22254                                     class : 'fa fa-chevron-left'
22255                                 }
22256                             ]
22257                         },
22258                         {
22259                             tag : 'div',
22260                             class : 'carousel-next',
22261                             cn : [
22262                                 {
22263                                     tag : 'i',
22264                                     class : 'fa fa-chevron-right'
22265                                 }
22266                             ]
22267                         }
22268                     ]
22269                 });
22270             }
22271             
22272         }
22273         
22274         return cfg;
22275     },
22276     
22277     initEvents:  function()
22278     {
22279 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22280 //            this.el.on("touchstart", this.onTouchStart, this);
22281 //        }
22282         
22283         if(this.autoslide){
22284             var _this = this;
22285             
22286             this.slideFn = window.setInterval(function() {
22287                 _this.showPanelNext();
22288             }, this.timer);
22289         }
22290         
22291         if(this.showarrow){
22292             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22293             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22294         }
22295         
22296         
22297     },
22298     
22299 //    onTouchStart : function(e, el, o)
22300 //    {
22301 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22302 //            return;
22303 //        }
22304 //        
22305 //        this.showPanelNext();
22306 //    },
22307     
22308     
22309     getChildContainer : function()
22310     {
22311         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22312     },
22313     
22314     /**
22315     * register a Navigation item
22316     * @param {Roo.bootstrap.nav.Item} the navitem to add
22317     */
22318     register : function(item)
22319     {
22320         this.tabs.push( item);
22321         item.navId = this.navId; // not really needed..
22322         this.addBullet();
22323     
22324     },
22325     
22326     getActivePanel : function()
22327     {
22328         var r = false;
22329         Roo.each(this.tabs, function(t) {
22330             if (t.active) {
22331                 r = t;
22332                 return false;
22333             }
22334             return null;
22335         });
22336         return r;
22337         
22338     },
22339     getPanelByName : function(n)
22340     {
22341         var r = false;
22342         Roo.each(this.tabs, function(t) {
22343             if (t.tabId == n) {
22344                 r = t;
22345                 return false;
22346             }
22347             return null;
22348         });
22349         return r;
22350     },
22351     indexOfPanel : function(p)
22352     {
22353         var r = false;
22354         Roo.each(this.tabs, function(t,i) {
22355             if (t.tabId == p.tabId) {
22356                 r = i;
22357                 return false;
22358             }
22359             return null;
22360         });
22361         return r;
22362     },
22363     /**
22364      * show a specific panel
22365      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22366      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22367      */
22368     showPanel : function (pan)
22369     {
22370         if(this.transition || typeof(pan) == 'undefined'){
22371             Roo.log("waiting for the transitionend");
22372             return false;
22373         }
22374         
22375         if (typeof(pan) == 'number') {
22376             pan = this.tabs[pan];
22377         }
22378         
22379         if (typeof(pan) == 'string') {
22380             pan = this.getPanelByName(pan);
22381         }
22382         
22383         var cur = this.getActivePanel();
22384         
22385         if(!pan || !cur){
22386             Roo.log('pan or acitve pan is undefined');
22387             return false;
22388         }
22389         
22390         if (pan.tabId == this.getActivePanel().tabId) {
22391             return true;
22392         }
22393         
22394         if (false === cur.fireEvent('beforedeactivate')) {
22395             return false;
22396         }
22397         
22398         if(this.bullets > 0 && !Roo.isTouch){
22399             this.setActiveBullet(this.indexOfPanel(pan));
22400         }
22401         
22402         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22403             
22404             //class="carousel-item carousel-item-next carousel-item-left"
22405             
22406             this.transition = true;
22407             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22408             var lr = dir == 'next' ? 'left' : 'right';
22409             pan.el.addClass(dir); // or prev
22410             pan.el.addClass('carousel-item-' + dir); // or prev
22411             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22412             cur.el.addClass(lr); // or right
22413             pan.el.addClass(lr);
22414             cur.el.addClass('carousel-item-' +lr); // or right
22415             pan.el.addClass('carousel-item-' +lr);
22416             
22417             
22418             var _this = this;
22419             cur.el.on('transitionend', function() {
22420                 Roo.log("trans end?");
22421                 
22422                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22423                 pan.setActive(true);
22424                 
22425                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22426                 cur.setActive(false);
22427                 
22428                 _this.transition = false;
22429                 
22430             }, this, { single:  true } );
22431             
22432             return true;
22433         }
22434         
22435         cur.setActive(false);
22436         pan.setActive(true);
22437         
22438         return true;
22439         
22440     },
22441     showPanelNext : function()
22442     {
22443         var i = this.indexOfPanel(this.getActivePanel());
22444         
22445         if (i >= this.tabs.length - 1 && !this.autoslide) {
22446             return;
22447         }
22448         
22449         if (i >= this.tabs.length - 1 && this.autoslide) {
22450             i = -1;
22451         }
22452         
22453         this.showPanel(this.tabs[i+1]);
22454     },
22455     
22456     showPanelPrev : function()
22457     {
22458         var i = this.indexOfPanel(this.getActivePanel());
22459         
22460         if (i  < 1 && !this.autoslide) {
22461             return;
22462         }
22463         
22464         if (i < 1 && this.autoslide) {
22465             i = this.tabs.length;
22466         }
22467         
22468         this.showPanel(this.tabs[i-1]);
22469     },
22470     
22471     
22472     addBullet: function()
22473     {
22474         if(!this.bullets || Roo.isTouch){
22475             return;
22476         }
22477         var ctr = this.el.select('.carousel-bullets',true).first();
22478         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22479         var bullet = ctr.createChild({
22480             cls : 'bullet bullet-' + i
22481         },ctr.dom.lastChild);
22482         
22483         
22484         var _this = this;
22485         
22486         bullet.on('click', (function(e, el, o, ii, t){
22487
22488             e.preventDefault();
22489
22490             this.showPanel(ii);
22491
22492             if(this.autoslide && this.slideFn){
22493                 clearInterval(this.slideFn);
22494                 this.slideFn = window.setInterval(function() {
22495                     _this.showPanelNext();
22496                 }, this.timer);
22497             }
22498
22499         }).createDelegate(this, [i, bullet], true));
22500                 
22501         
22502     },
22503      
22504     setActiveBullet : function(i)
22505     {
22506         if(Roo.isTouch){
22507             return;
22508         }
22509         
22510         Roo.each(this.el.select('.bullet', true).elements, function(el){
22511             el.removeClass('selected');
22512         });
22513
22514         var bullet = this.el.select('.bullet-' + i, true).first();
22515         
22516         if(!bullet){
22517             return;
22518         }
22519         
22520         bullet.addClass('selected');
22521     }
22522     
22523     
22524   
22525 });
22526
22527  
22528
22529  
22530  
22531 Roo.apply(Roo.bootstrap.TabGroup, {
22532     
22533     groups: {},
22534      /**
22535     * register a Navigation Group
22536     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22537     */
22538     register : function(navgrp)
22539     {
22540         this.groups[navgrp.navId] = navgrp;
22541         
22542     },
22543     /**
22544     * fetch a Navigation Group based on the navigation ID
22545     * if one does not exist , it will get created.
22546     * @param {string} the navgroup to add
22547     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22548     */
22549     get: function(navId) {
22550         if (typeof(this.groups[navId]) == 'undefined') {
22551             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22552         }
22553         return this.groups[navId] ;
22554     }
22555     
22556     
22557     
22558 });
22559
22560  /*
22561  * - LGPL
22562  *
22563  * TabPanel
22564  * 
22565  */
22566
22567 /**
22568  * @class Roo.bootstrap.TabPanel
22569  * @extends Roo.bootstrap.Component
22570  * @children Roo.bootstrap.Component
22571  * Bootstrap TabPanel class
22572  * @cfg {Boolean} active panel active
22573  * @cfg {String} html panel content
22574  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22575  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22576  * @cfg {String} href click to link..
22577  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22578  * 
22579  * 
22580  * @constructor
22581  * Create a new TabPanel
22582  * @param {Object} config The config object
22583  */
22584
22585 Roo.bootstrap.TabPanel = function(config){
22586     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22587     this.addEvents({
22588         /**
22589              * @event changed
22590              * Fires when the active status changes
22591              * @param {Roo.bootstrap.TabPanel} this
22592              * @param {Boolean} state the new state
22593             
22594          */
22595         'changed': true,
22596         /**
22597              * @event beforedeactivate
22598              * Fires before a tab is de-activated - can be used to do validation on a form.
22599              * @param {Roo.bootstrap.TabPanel} this
22600              * @return {Boolean} false if there is an error
22601             
22602          */
22603         'beforedeactivate': true
22604      });
22605     
22606     this.tabId = this.tabId || Roo.id();
22607   
22608 };
22609
22610 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22611     
22612     active: false,
22613     html: false,
22614     tabId: false,
22615     navId : false,
22616     href : '',
22617     touchSlide : false,
22618     getAutoCreate : function(){
22619         
22620         
22621         var cfg = {
22622             tag: 'div',
22623             // item is needed for carousel - not sure if it has any effect otherwise
22624             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22625             html: this.html || ''
22626         };
22627         
22628         if(this.active){
22629             cfg.cls += ' active';
22630         }
22631         
22632         if(this.tabId){
22633             cfg.tabId = this.tabId;
22634         }
22635         
22636         
22637         
22638         return cfg;
22639     },
22640     
22641     initEvents:  function()
22642     {
22643         var p = this.parent();
22644         
22645         this.navId = this.navId || p.navId;
22646         
22647         if (typeof(this.navId) != 'undefined') {
22648             // not really needed.. but just in case.. parent should be a NavGroup.
22649             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22650             
22651             tg.register(this);
22652             
22653             var i = tg.tabs.length - 1;
22654             
22655             if(this.active && tg.bullets > 0 && i < tg.bullets){
22656                 tg.setActiveBullet(i);
22657             }
22658         }
22659         
22660         this.el.on('click', this.onClick, this);
22661         
22662         if(Roo.isTouch && this.touchSlide){
22663             this.el.on("touchstart", this.onTouchStart, this);
22664             this.el.on("touchmove", this.onTouchMove, this);
22665             this.el.on("touchend", this.onTouchEnd, this);
22666         }
22667         
22668     },
22669     
22670     onRender : function(ct, position)
22671     {
22672         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22673     },
22674     
22675     setActive : function(state)
22676     {
22677         Roo.log("panel - set active " + this.tabId + "=" + state);
22678         
22679         this.active = state;
22680         if (!state) {
22681             this.el.removeClass('active');
22682             
22683         } else  if (!this.el.hasClass('active')) {
22684             this.el.addClass('active');
22685         }
22686         
22687         this.fireEvent('changed', this, state);
22688     },
22689     
22690     onClick : function(e)
22691     {
22692         e.preventDefault();
22693         
22694         if(!this.href.length){
22695             return;
22696         }
22697         
22698         window.location.href = this.href;
22699     },
22700     
22701     startX : 0,
22702     startY : 0,
22703     endX : 0,
22704     endY : 0,
22705     swiping : false,
22706     
22707     onTouchStart : function(e)
22708     {
22709         this.swiping = false;
22710         
22711         this.startX = e.browserEvent.touches[0].clientX;
22712         this.startY = e.browserEvent.touches[0].clientY;
22713     },
22714     
22715     onTouchMove : function(e)
22716     {
22717         this.swiping = true;
22718         
22719         this.endX = e.browserEvent.touches[0].clientX;
22720         this.endY = e.browserEvent.touches[0].clientY;
22721     },
22722     
22723     onTouchEnd : function(e)
22724     {
22725         if(!this.swiping){
22726             this.onClick(e);
22727             return;
22728         }
22729         
22730         var tabGroup = this.parent();
22731         
22732         if(this.endX > this.startX){ // swiping right
22733             tabGroup.showPanelPrev();
22734             return;
22735         }
22736         
22737         if(this.startX > this.endX){ // swiping left
22738             tabGroup.showPanelNext();
22739             return;
22740         }
22741     }
22742     
22743     
22744 });
22745  
22746
22747  
22748
22749  /*
22750  * - LGPL
22751  *
22752  * DateField
22753  * 
22754  */
22755
22756 /**
22757  * @class Roo.bootstrap.form.DateField
22758  * @extends Roo.bootstrap.form.Input
22759  * Bootstrap DateField class
22760  * @cfg {Number} weekStart default 0
22761  * @cfg {String} viewMode default empty, (months|years)
22762  * @cfg {String} minViewMode default empty, (months|years)
22763  * @cfg {Number} startDate default -Infinity
22764  * @cfg {Number} endDate default Infinity
22765  * @cfg {Boolean} todayHighlight default false
22766  * @cfg {Boolean} todayBtn default false
22767  * @cfg {Boolean} calendarWeeks default false
22768  * @cfg {Object} daysOfWeekDisabled default empty
22769  * @cfg {Boolean} singleMode default false (true | false)
22770  * 
22771  * @cfg {Boolean} keyboardNavigation default true
22772  * @cfg {String} language default en
22773  * 
22774  * @constructor
22775  * Create a new DateField
22776  * @param {Object} config The config object
22777  */
22778
22779 Roo.bootstrap.form.DateField = function(config){
22780     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22781      this.addEvents({
22782             /**
22783              * @event show
22784              * Fires when this field show.
22785              * @param {Roo.bootstrap.form.DateField} this
22786              * @param {Mixed} date The date value
22787              */
22788             show : true,
22789             /**
22790              * @event show
22791              * Fires when this field hide.
22792              * @param {Roo.bootstrap.form.DateField} this
22793              * @param {Mixed} date The date value
22794              */
22795             hide : true,
22796             /**
22797              * @event select
22798              * Fires when select a date.
22799              * @param {Roo.bootstrap.form.DateField} this
22800              * @param {Mixed} date The date value
22801              */
22802             select : true,
22803             /**
22804              * @event beforeselect
22805              * Fires when before select a date.
22806              * @param {Roo.bootstrap.form.DateField} this
22807              * @param {Mixed} date The date value
22808              */
22809             beforeselect : true
22810         });
22811 };
22812
22813 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22814     
22815     /**
22816      * @cfg {String} format
22817      * The default date format string which can be overriden for localization support.  The format must be
22818      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22819      */
22820     format : "m/d/y",
22821     /**
22822      * @cfg {String} altFormats
22823      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22824      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22825      */
22826     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22827     
22828     weekStart : 0,
22829     
22830     viewMode : '',
22831     
22832     minViewMode : '',
22833     
22834     todayHighlight : false,
22835     
22836     todayBtn: false,
22837     
22838     language: 'en',
22839     
22840     keyboardNavigation: true,
22841     
22842     calendarWeeks: false,
22843     
22844     startDate: -Infinity,
22845     
22846     endDate: Infinity,
22847     
22848     daysOfWeekDisabled: [],
22849     
22850     _events: [],
22851     
22852     singleMode : false,
22853     
22854     UTCDate: function()
22855     {
22856         return new Date(Date.UTC.apply(Date, arguments));
22857     },
22858     
22859     UTCToday: function()
22860     {
22861         var today = new Date();
22862         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22863     },
22864     
22865     getDate: function() {
22866             var d = this.getUTCDate();
22867             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22868     },
22869     
22870     getUTCDate: function() {
22871             return this.date;
22872     },
22873     
22874     setDate: function(d) {
22875             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22876     },
22877     
22878     setUTCDate: function(d) {
22879             this.date = d;
22880             this.setValue(this.formatDate(this.date));
22881     },
22882         
22883     onRender: function(ct, position)
22884     {
22885         
22886         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22887         
22888         this.language = this.language || 'en';
22889         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22890         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22891         
22892         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22893         this.format = this.format || 'm/d/y';
22894         this.isInline = false;
22895         this.isInput = true;
22896         this.component = this.el.select('.add-on', true).first() || false;
22897         this.component = (this.component && this.component.length === 0) ? false : this.component;
22898         this.hasInput = this.component && this.inputEl().length;
22899         
22900         if (typeof(this.minViewMode === 'string')) {
22901             switch (this.minViewMode) {
22902                 case 'months':
22903                     this.minViewMode = 1;
22904                     break;
22905                 case 'years':
22906                     this.minViewMode = 2;
22907                     break;
22908                 default:
22909                     this.minViewMode = 0;
22910                     break;
22911             }
22912         }
22913         
22914         if (typeof(this.viewMode === 'string')) {
22915             switch (this.viewMode) {
22916                 case 'months':
22917                     this.viewMode = 1;
22918                     break;
22919                 case 'years':
22920                     this.viewMode = 2;
22921                     break;
22922                 default:
22923                     this.viewMode = 0;
22924                     break;
22925             }
22926         }
22927                 
22928         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22929         
22930 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22931         
22932         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22933         
22934         this.picker().on('mousedown', this.onMousedown, this);
22935         this.picker().on('click', this.onClick, this);
22936         
22937         this.picker().addClass('datepicker-dropdown');
22938         
22939         this.startViewMode = this.viewMode;
22940         
22941         if(this.singleMode){
22942             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22943                 v.setVisibilityMode(Roo.Element.DISPLAY);
22944                 v.hide();
22945             });
22946             
22947             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22948                 v.setStyle('width', '189px');
22949             });
22950         }
22951         
22952         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22953             if(!this.calendarWeeks){
22954                 v.remove();
22955                 return;
22956             }
22957             
22958             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22959             v.attr('colspan', function(i, val){
22960                 return parseInt(val) + 1;
22961             });
22962         });
22963                         
22964         
22965         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22966         
22967         this.setStartDate(this.startDate);
22968         this.setEndDate(this.endDate);
22969         
22970         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22971         
22972         this.fillDow();
22973         this.fillMonths();
22974         this.update();
22975         this.showMode();
22976         
22977         if(this.isInline) {
22978             this.showPopup();
22979         }
22980     },
22981     
22982     picker : function()
22983     {
22984         return this.pickerEl;
22985 //        return this.el.select('.datepicker', true).first();
22986     },
22987     
22988     fillDow: function()
22989     {
22990         var dowCnt = this.weekStart;
22991         
22992         var dow = {
22993             tag: 'tr',
22994             cn: [
22995                 
22996             ]
22997         };
22998         
22999         if(this.calendarWeeks){
23000             dow.cn.push({
23001                 tag: 'th',
23002                 cls: 'cw',
23003                 html: '&nbsp;'
23004             })
23005         }
23006         
23007         while (dowCnt < this.weekStart + 7) {
23008             dow.cn.push({
23009                 tag: 'th',
23010                 cls: 'dow',
23011                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23012             });
23013         }
23014         
23015         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23016     },
23017     
23018     fillMonths: function()
23019     {    
23020         var i = 0;
23021         var months = this.picker().select('>.datepicker-months td', true).first();
23022         
23023         months.dom.innerHTML = '';
23024         
23025         while (i < 12) {
23026             var month = {
23027                 tag: 'span',
23028                 cls: 'month',
23029                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23030             };
23031             
23032             months.createChild(month);
23033         }
23034         
23035     },
23036     
23037     update: function()
23038     {
23039         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;
23040         
23041         if (this.date < this.startDate) {
23042             this.viewDate = new Date(this.startDate);
23043         } else if (this.date > this.endDate) {
23044             this.viewDate = new Date(this.endDate);
23045         } else {
23046             this.viewDate = new Date(this.date);
23047         }
23048         
23049         this.fill();
23050     },
23051     
23052     fill: function() 
23053     {
23054         var d = new Date(this.viewDate),
23055                 year = d.getUTCFullYear(),
23056                 month = d.getUTCMonth(),
23057                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23058                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23059                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23060                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23061                 currentDate = this.date && this.date.valueOf(),
23062                 today = this.UTCToday();
23063         
23064         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23065         
23066 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23067         
23068 //        this.picker.select('>tfoot th.today').
23069 //                                              .text(dates[this.language].today)
23070 //                                              .toggle(this.todayBtn !== false);
23071     
23072         this.updateNavArrows();
23073         this.fillMonths();
23074                                                 
23075         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23076         
23077         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23078          
23079         prevMonth.setUTCDate(day);
23080         
23081         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23082         
23083         var nextMonth = new Date(prevMonth);
23084         
23085         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23086         
23087         nextMonth = nextMonth.valueOf();
23088         
23089         var fillMonths = false;
23090         
23091         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23092         
23093         while(prevMonth.valueOf() <= nextMonth) {
23094             var clsName = '';
23095             
23096             if (prevMonth.getUTCDay() === this.weekStart) {
23097                 if(fillMonths){
23098                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23099                 }
23100                     
23101                 fillMonths = {
23102                     tag: 'tr',
23103                     cn: []
23104                 };
23105                 
23106                 if(this.calendarWeeks){
23107                     // ISO 8601: First week contains first thursday.
23108                     // ISO also states week starts on Monday, but we can be more abstract here.
23109                     var
23110                     // Start of current week: based on weekstart/current date
23111                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23112                     // Thursday of this week
23113                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23114                     // First Thursday of year, year from thursday
23115                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23116                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23117                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23118                     
23119                     fillMonths.cn.push({
23120                         tag: 'td',
23121                         cls: 'cw',
23122                         html: calWeek
23123                     });
23124                 }
23125             }
23126             
23127             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23128                 clsName += ' old';
23129             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23130                 clsName += ' new';
23131             }
23132             if (this.todayHighlight &&
23133                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23134                 prevMonth.getUTCMonth() == today.getMonth() &&
23135                 prevMonth.getUTCDate() == today.getDate()) {
23136                 clsName += ' today';
23137             }
23138             
23139             if (currentDate && prevMonth.valueOf() === currentDate) {
23140                 clsName += ' active';
23141             }
23142             
23143             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23144                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23145                     clsName += ' disabled';
23146             }
23147             
23148             fillMonths.cn.push({
23149                 tag: 'td',
23150                 cls: 'day ' + clsName,
23151                 html: prevMonth.getDate()
23152             });
23153             
23154             prevMonth.setDate(prevMonth.getDate()+1);
23155         }
23156           
23157         var currentYear = this.date && this.date.getUTCFullYear();
23158         var currentMonth = this.date && this.date.getUTCMonth();
23159         
23160         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23161         
23162         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23163             v.removeClass('active');
23164             
23165             if(currentYear === year && k === currentMonth){
23166                 v.addClass('active');
23167             }
23168             
23169             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23170                 v.addClass('disabled');
23171             }
23172             
23173         });
23174         
23175         
23176         year = parseInt(year/10, 10) * 10;
23177         
23178         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23179         
23180         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23181         
23182         year -= 1;
23183         for (var i = -1; i < 11; i++) {
23184             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23185                 tag: 'span',
23186                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23187                 html: year
23188             });
23189             
23190             year += 1;
23191         }
23192     },
23193     
23194     showMode: function(dir) 
23195     {
23196         if (dir) {
23197             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23198         }
23199         
23200         Roo.each(this.picker().select('>div',true).elements, function(v){
23201             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23202             v.hide();
23203         });
23204         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23205     },
23206     
23207     place: function()
23208     {
23209         if(this.isInline) {
23210             return;
23211         }
23212         
23213         this.picker().removeClass(['bottom', 'top']);
23214         
23215         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23216             /*
23217              * place to the top of element!
23218              *
23219              */
23220             
23221             this.picker().addClass('top');
23222             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23223             
23224             return;
23225         }
23226         
23227         this.picker().addClass('bottom');
23228         
23229         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23230     },
23231     
23232     parseDate : function(value)
23233     {
23234         if(!value || value instanceof Date){
23235             return value;
23236         }
23237         var v = Date.parseDate(value, this.format);
23238         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23239             v = Date.parseDate(value, 'Y-m-d');
23240         }
23241         if(!v && this.altFormats){
23242             if(!this.altFormatsArray){
23243                 this.altFormatsArray = this.altFormats.split("|");
23244             }
23245             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23246                 v = Date.parseDate(value, this.altFormatsArray[i]);
23247             }
23248         }
23249         return v;
23250     },
23251     
23252     formatDate : function(date, fmt)
23253     {   
23254         return (!date || !(date instanceof Date)) ?
23255         date : date.dateFormat(fmt || this.format);
23256     },
23257     
23258     onFocus : function()
23259     {
23260         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23261         this.showPopup();
23262     },
23263     
23264     onBlur : function()
23265     {
23266         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23267         
23268         var d = this.inputEl().getValue();
23269         
23270         this.setValue(d);
23271                 
23272         this.hidePopup();
23273     },
23274     
23275     showPopup : function()
23276     {
23277         this.picker().show();
23278         this.update();
23279         this.place();
23280         
23281         this.fireEvent('showpopup', this, this.date);
23282     },
23283     
23284     hidePopup : function()
23285     {
23286         if(this.isInline) {
23287             return;
23288         }
23289         this.picker().hide();
23290         this.viewMode = this.startViewMode;
23291         this.showMode();
23292         
23293         this.fireEvent('hidepopup', this, this.date);
23294         
23295     },
23296     
23297     onMousedown: function(e)
23298     {
23299         e.stopPropagation();
23300         e.preventDefault();
23301     },
23302     
23303     keyup: function(e)
23304     {
23305         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23306         this.update();
23307     },
23308
23309     setValue: function(v)
23310     {
23311         if(this.fireEvent('beforeselect', this, v) !== false){
23312             var d = new Date(this.parseDate(v) ).clearTime();
23313         
23314             if(isNaN(d.getTime())){
23315                 this.date = this.viewDate = '';
23316                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23317                 return;
23318             }
23319
23320             v = this.formatDate(d);
23321
23322             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23323
23324             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23325
23326             this.update();
23327
23328             this.fireEvent('select', this, this.date);
23329         }
23330     },
23331     
23332     getValue: function()
23333     {
23334         return this.formatDate(this.date);
23335     },
23336     
23337     fireKey: function(e)
23338     {
23339         if (!this.picker().isVisible()){
23340             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23341                 this.showPopup();
23342             }
23343             return;
23344         }
23345         
23346         var dateChanged = false,
23347         dir, day, month,
23348         newDate, newViewDate;
23349         
23350         switch(e.keyCode){
23351             case 27: // escape
23352                 this.hidePopup();
23353                 e.preventDefault();
23354                 break;
23355             case 37: // left
23356             case 39: // right
23357                 if (!this.keyboardNavigation) {
23358                     break;
23359                 }
23360                 dir = e.keyCode == 37 ? -1 : 1;
23361                 
23362                 if (e.ctrlKey){
23363                     newDate = this.moveYear(this.date, dir);
23364                     newViewDate = this.moveYear(this.viewDate, dir);
23365                 } else if (e.shiftKey){
23366                     newDate = this.moveMonth(this.date, dir);
23367                     newViewDate = this.moveMonth(this.viewDate, dir);
23368                 } else {
23369                     newDate = new Date(this.date);
23370                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23371                     newViewDate = new Date(this.viewDate);
23372                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23373                 }
23374                 if (this.dateWithinRange(newDate)){
23375                     this.date = newDate;
23376                     this.viewDate = newViewDate;
23377                     this.setValue(this.formatDate(this.date));
23378 //                    this.update();
23379                     e.preventDefault();
23380                     dateChanged = true;
23381                 }
23382                 break;
23383             case 38: // up
23384             case 40: // down
23385                 if (!this.keyboardNavigation) {
23386                     break;
23387                 }
23388                 dir = e.keyCode == 38 ? -1 : 1;
23389                 if (e.ctrlKey){
23390                     newDate = this.moveYear(this.date, dir);
23391                     newViewDate = this.moveYear(this.viewDate, dir);
23392                 } else if (e.shiftKey){
23393                     newDate = this.moveMonth(this.date, dir);
23394                     newViewDate = this.moveMonth(this.viewDate, dir);
23395                 } else {
23396                     newDate = new Date(this.date);
23397                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23398                     newViewDate = new Date(this.viewDate);
23399                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23400                 }
23401                 if (this.dateWithinRange(newDate)){
23402                     this.date = newDate;
23403                     this.viewDate = newViewDate;
23404                     this.setValue(this.formatDate(this.date));
23405 //                    this.update();
23406                     e.preventDefault();
23407                     dateChanged = true;
23408                 }
23409                 break;
23410             case 13: // enter
23411                 this.setValue(this.formatDate(this.date));
23412                 this.hidePopup();
23413                 e.preventDefault();
23414                 break;
23415             case 9: // tab
23416                 this.setValue(this.formatDate(this.date));
23417                 this.hidePopup();
23418                 break;
23419             case 16: // shift
23420             case 17: // ctrl
23421             case 18: // alt
23422                 break;
23423             default :
23424                 this.hidePopup();
23425                 
23426         }
23427     },
23428     
23429     
23430     onClick: function(e) 
23431     {
23432         e.stopPropagation();
23433         e.preventDefault();
23434         
23435         var target = e.getTarget();
23436         
23437         if(target.nodeName.toLowerCase() === 'i'){
23438             target = Roo.get(target).dom.parentNode;
23439         }
23440         
23441         var nodeName = target.nodeName;
23442         var className = target.className;
23443         var html = target.innerHTML;
23444         //Roo.log(nodeName);
23445         
23446         switch(nodeName.toLowerCase()) {
23447             case 'th':
23448                 switch(className) {
23449                     case 'switch':
23450                         this.showMode(1);
23451                         break;
23452                     case 'prev':
23453                     case 'next':
23454                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23455                         switch(this.viewMode){
23456                                 case 0:
23457                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23458                                         break;
23459                                 case 1:
23460                                 case 2:
23461                                         this.viewDate = this.moveYear(this.viewDate, dir);
23462                                         break;
23463                         }
23464                         this.fill();
23465                         break;
23466                     case 'today':
23467                         var date = new Date();
23468                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23469 //                        this.fill()
23470                         this.setValue(this.formatDate(this.date));
23471                         
23472                         this.hidePopup();
23473                         break;
23474                 }
23475                 break;
23476             case 'span':
23477                 if (className.indexOf('disabled') < 0) {
23478                 if (!this.viewDate) {
23479                     this.viewDate = new Date();
23480                 }
23481                 this.viewDate.setUTCDate(1);
23482                     if (className.indexOf('month') > -1) {
23483                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23484                     } else {
23485                         var year = parseInt(html, 10) || 0;
23486                         this.viewDate.setUTCFullYear(year);
23487                         
23488                     }
23489                     
23490                     if(this.singleMode){
23491                         this.setValue(this.formatDate(this.viewDate));
23492                         this.hidePopup();
23493                         return;
23494                     }
23495                     
23496                     this.showMode(-1);
23497                     this.fill();
23498                 }
23499                 break;
23500                 
23501             case 'td':
23502                 //Roo.log(className);
23503                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23504                     var day = parseInt(html, 10) || 1;
23505                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23506                         month = (this.viewDate || new Date()).getUTCMonth();
23507
23508                     if (className.indexOf('old') > -1) {
23509                         if(month === 0 ){
23510                             month = 11;
23511                             year -= 1;
23512                         }else{
23513                             month -= 1;
23514                         }
23515                     } else if (className.indexOf('new') > -1) {
23516                         if (month == 11) {
23517                             month = 0;
23518                             year += 1;
23519                         } else {
23520                             month += 1;
23521                         }
23522                     }
23523                     //Roo.log([year,month,day]);
23524                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23525                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23526 //                    this.fill();
23527                     //Roo.log(this.formatDate(this.date));
23528                     this.setValue(this.formatDate(this.date));
23529                     this.hidePopup();
23530                 }
23531                 break;
23532         }
23533     },
23534     
23535     setStartDate: function(startDate)
23536     {
23537         this.startDate = startDate || -Infinity;
23538         if (this.startDate !== -Infinity) {
23539             this.startDate = this.parseDate(this.startDate);
23540         }
23541         this.update();
23542         this.updateNavArrows();
23543     },
23544
23545     setEndDate: function(endDate)
23546     {
23547         this.endDate = endDate || Infinity;
23548         if (this.endDate !== Infinity) {
23549             this.endDate = this.parseDate(this.endDate);
23550         }
23551         this.update();
23552         this.updateNavArrows();
23553     },
23554     
23555     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23556     {
23557         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23558         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23559             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23560         }
23561         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23562             return parseInt(d, 10);
23563         });
23564         this.update();
23565         this.updateNavArrows();
23566     },
23567     
23568     updateNavArrows: function() 
23569     {
23570         if(this.singleMode){
23571             return;
23572         }
23573         
23574         var d = new Date(this.viewDate),
23575         year = d.getUTCFullYear(),
23576         month = d.getUTCMonth();
23577         
23578         Roo.each(this.picker().select('.prev', true).elements, function(v){
23579             v.show();
23580             switch (this.viewMode) {
23581                 case 0:
23582
23583                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23584                         v.hide();
23585                     }
23586                     break;
23587                 case 1:
23588                 case 2:
23589                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23590                         v.hide();
23591                     }
23592                     break;
23593             }
23594         });
23595         
23596         Roo.each(this.picker().select('.next', true).elements, function(v){
23597             v.show();
23598             switch (this.viewMode) {
23599                 case 0:
23600
23601                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23602                         v.hide();
23603                     }
23604                     break;
23605                 case 1:
23606                 case 2:
23607                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23608                         v.hide();
23609                     }
23610                     break;
23611             }
23612         })
23613     },
23614     
23615     moveMonth: function(date, dir)
23616     {
23617         if (!dir) {
23618             return date;
23619         }
23620         var new_date = new Date(date.valueOf()),
23621         day = new_date.getUTCDate(),
23622         month = new_date.getUTCMonth(),
23623         mag = Math.abs(dir),
23624         new_month, test;
23625         dir = dir > 0 ? 1 : -1;
23626         if (mag == 1){
23627             test = dir == -1
23628             // If going back one month, make sure month is not current month
23629             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23630             ? function(){
23631                 return new_date.getUTCMonth() == month;
23632             }
23633             // If going forward one month, make sure month is as expected
23634             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23635             : function(){
23636                 return new_date.getUTCMonth() != new_month;
23637             };
23638             new_month = month + dir;
23639             new_date.setUTCMonth(new_month);
23640             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23641             if (new_month < 0 || new_month > 11) {
23642                 new_month = (new_month + 12) % 12;
23643             }
23644         } else {
23645             // For magnitudes >1, move one month at a time...
23646             for (var i=0; i<mag; i++) {
23647                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23648                 new_date = this.moveMonth(new_date, dir);
23649             }
23650             // ...then reset the day, keeping it in the new month
23651             new_month = new_date.getUTCMonth();
23652             new_date.setUTCDate(day);
23653             test = function(){
23654                 return new_month != new_date.getUTCMonth();
23655             };
23656         }
23657         // Common date-resetting loop -- if date is beyond end of month, make it
23658         // end of month
23659         while (test()){
23660             new_date.setUTCDate(--day);
23661             new_date.setUTCMonth(new_month);
23662         }
23663         return new_date;
23664     },
23665
23666     moveYear: function(date, dir)
23667     {
23668         return this.moveMonth(date, dir*12);
23669     },
23670
23671     dateWithinRange: function(date)
23672     {
23673         return date >= this.startDate && date <= this.endDate;
23674     },
23675
23676     
23677     remove: function() 
23678     {
23679         this.picker().remove();
23680     },
23681     
23682     validateValue : function(value)
23683     {
23684         if(this.getVisibilityEl().hasClass('hidden')){
23685             return true;
23686         }
23687         
23688         if(value.length < 1)  {
23689             if(this.allowBlank){
23690                 return true;
23691             }
23692             return false;
23693         }
23694         
23695         if(value.length < this.minLength){
23696             return false;
23697         }
23698         if(value.length > this.maxLength){
23699             return false;
23700         }
23701         if(this.vtype){
23702             var vt = Roo.form.VTypes;
23703             if(!vt[this.vtype](value, this)){
23704                 return false;
23705             }
23706         }
23707         if(typeof this.validator == "function"){
23708             var msg = this.validator(value);
23709             if(msg !== true){
23710                 return false;
23711             }
23712         }
23713         
23714         if(this.regex && !this.regex.test(value)){
23715             return false;
23716         }
23717         
23718         if(typeof(this.parseDate(value)) == 'undefined'){
23719             return false;
23720         }
23721         
23722         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23723             return false;
23724         }      
23725         
23726         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23727             return false;
23728         } 
23729         
23730         
23731         return true;
23732     },
23733     
23734     reset : function()
23735     {
23736         this.date = this.viewDate = '';
23737         
23738         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23739     }
23740    
23741 });
23742
23743 Roo.apply(Roo.bootstrap.form.DateField,  {
23744     
23745     head : {
23746         tag: 'thead',
23747         cn: [
23748         {
23749             tag: 'tr',
23750             cn: [
23751             {
23752                 tag: 'th',
23753                 cls: 'prev',
23754                 html: '<i class="fa fa-arrow-left"/>'
23755             },
23756             {
23757                 tag: 'th',
23758                 cls: 'switch',
23759                 colspan: '5'
23760             },
23761             {
23762                 tag: 'th',
23763                 cls: 'next',
23764                 html: '<i class="fa fa-arrow-right"/>'
23765             }
23766
23767             ]
23768         }
23769         ]
23770     },
23771     
23772     content : {
23773         tag: 'tbody',
23774         cn: [
23775         {
23776             tag: 'tr',
23777             cn: [
23778             {
23779                 tag: 'td',
23780                 colspan: '7'
23781             }
23782             ]
23783         }
23784         ]
23785     },
23786     
23787     footer : {
23788         tag: 'tfoot',
23789         cn: [
23790         {
23791             tag: 'tr',
23792             cn: [
23793             {
23794                 tag: 'th',
23795                 colspan: '7',
23796                 cls: 'today'
23797             }
23798                     
23799             ]
23800         }
23801         ]
23802     },
23803     
23804     dates:{
23805         en: {
23806             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23807             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23808             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23809             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23810             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23811             today: "Today"
23812         }
23813     },
23814     
23815     modes: [
23816     {
23817         clsName: 'days',
23818         navFnc: 'Month',
23819         navStep: 1
23820     },
23821     {
23822         clsName: 'months',
23823         navFnc: 'FullYear',
23824         navStep: 1
23825     },
23826     {
23827         clsName: 'years',
23828         navFnc: 'FullYear',
23829         navStep: 10
23830     }]
23831 });
23832
23833 Roo.apply(Roo.bootstrap.form.DateField,  {
23834   
23835     template : {
23836         tag: 'div',
23837         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23838         cn: [
23839         {
23840             tag: 'div',
23841             cls: 'datepicker-days',
23842             cn: [
23843             {
23844                 tag: 'table',
23845                 cls: 'table-condensed',
23846                 cn:[
23847                 Roo.bootstrap.form.DateField.head,
23848                 {
23849                     tag: 'tbody'
23850                 },
23851                 Roo.bootstrap.form.DateField.footer
23852                 ]
23853             }
23854             ]
23855         },
23856         {
23857             tag: 'div',
23858             cls: 'datepicker-months',
23859             cn: [
23860             {
23861                 tag: 'table',
23862                 cls: 'table-condensed',
23863                 cn:[
23864                 Roo.bootstrap.form.DateField.head,
23865                 Roo.bootstrap.form.DateField.content,
23866                 Roo.bootstrap.form.DateField.footer
23867                 ]
23868             }
23869             ]
23870         },
23871         {
23872             tag: 'div',
23873             cls: 'datepicker-years',
23874             cn: [
23875             {
23876                 tag: 'table',
23877                 cls: 'table-condensed',
23878                 cn:[
23879                 Roo.bootstrap.form.DateField.head,
23880                 Roo.bootstrap.form.DateField.content,
23881                 Roo.bootstrap.form.DateField.footer
23882                 ]
23883             }
23884             ]
23885         }
23886         ]
23887     }
23888 });
23889
23890  
23891
23892  /*
23893  * - LGPL
23894  *
23895  * TimeField
23896  * 
23897  */
23898
23899 /**
23900  * @class Roo.bootstrap.form.TimeField
23901  * @extends Roo.bootstrap.form.Input
23902  * Bootstrap DateField class
23903  * 
23904  * 
23905  * @constructor
23906  * Create a new TimeField
23907  * @param {Object} config The config object
23908  */
23909
23910 Roo.bootstrap.form.TimeField = function(config){
23911     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23912     this.addEvents({
23913             /**
23914              * @event show
23915              * Fires when this field show.
23916              * @param {Roo.bootstrap.form.DateField} thisthis
23917              * @param {Mixed} date The date value
23918              */
23919             show : true,
23920             /**
23921              * @event show
23922              * Fires when this field hide.
23923              * @param {Roo.bootstrap.form.DateField} this
23924              * @param {Mixed} date The date value
23925              */
23926             hide : true,
23927             /**
23928              * @event select
23929              * Fires when select a date.
23930              * @param {Roo.bootstrap.form.DateField} this
23931              * @param {Mixed} date The date value
23932              */
23933             select : true
23934         });
23935 };
23936
23937 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23938     
23939     /**
23940      * @cfg {String} format
23941      * The default time format string which can be overriden for localization support.  The format must be
23942      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23943      */
23944     format : "H:i",
23945
23946     getAutoCreate : function()
23947     {
23948         this.after = '<i class="fa far fa-clock"></i>';
23949         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23950         
23951          
23952     },
23953     onRender: function(ct, position)
23954     {
23955         
23956         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23957                 
23958         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23959         
23960         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23961         
23962         this.pop = this.picker().select('>.datepicker-time',true).first();
23963         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23964         
23965         this.picker().on('mousedown', this.onMousedown, this);
23966         this.picker().on('click', this.onClick, this);
23967         
23968         this.picker().addClass('datepicker-dropdown');
23969     
23970         this.fillTime();
23971         this.update();
23972             
23973         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23974         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23975         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23976         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23977         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23978         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23979
23980     },
23981     
23982     fireKey: function(e){
23983         if (!this.picker().isVisible()){
23984             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23985                 this.show();
23986             }
23987             return;
23988         }
23989
23990         e.preventDefault();
23991         
23992         switch(e.keyCode){
23993             case 27: // escape
23994                 this.hide();
23995                 break;
23996             case 37: // left
23997             case 39: // right
23998                 this.onTogglePeriod();
23999                 break;
24000             case 38: // up
24001                 this.onIncrementMinutes();
24002                 break;
24003             case 40: // down
24004                 this.onDecrementMinutes();
24005                 break;
24006             case 13: // enter
24007             case 9: // tab
24008                 this.setTime();
24009                 break;
24010         }
24011     },
24012     
24013     onClick: function(e) {
24014         e.stopPropagation();
24015         e.preventDefault();
24016     },
24017     
24018     picker : function()
24019     {
24020         return this.pickerEl;
24021     },
24022     
24023     fillTime: function()
24024     {    
24025         var time = this.pop.select('tbody', true).first();
24026         
24027         time.dom.innerHTML = '';
24028         
24029         time.createChild({
24030             tag: 'tr',
24031             cn: [
24032                 {
24033                     tag: 'td',
24034                     cn: [
24035                         {
24036                             tag: 'a',
24037                             href: '#',
24038                             cls: 'btn',
24039                             cn: [
24040                                 {
24041                                     tag: 'i',
24042                                     cls: 'hours-up fa fas fa-chevron-up'
24043                                 }
24044                             ]
24045                         } 
24046                     ]
24047                 },
24048                 {
24049                     tag: 'td',
24050                     cls: 'separator'
24051                 },
24052                 {
24053                     tag: 'td',
24054                     cn: [
24055                         {
24056                             tag: 'a',
24057                             href: '#',
24058                             cls: 'btn',
24059                             cn: [
24060                                 {
24061                                     tag: 'i',
24062                                     cls: 'minutes-up fa fas fa-chevron-up'
24063                                 }
24064                             ]
24065                         }
24066                     ]
24067                 },
24068                 {
24069                     tag: 'td',
24070                     cls: 'separator'
24071                 }
24072             ]
24073         });
24074         
24075         time.createChild({
24076             tag: 'tr',
24077             cn: [
24078                 {
24079                     tag: 'td',
24080                     cn: [
24081                         {
24082                             tag: 'span',
24083                             cls: 'timepicker-hour',
24084                             html: '00'
24085                         }  
24086                     ]
24087                 },
24088                 {
24089                     tag: 'td',
24090                     cls: 'separator',
24091                     html: ':'
24092                 },
24093                 {
24094                     tag: 'td',
24095                     cn: [
24096                         {
24097                             tag: 'span',
24098                             cls: 'timepicker-minute',
24099                             html: '00'
24100                         }  
24101                     ]
24102                 },
24103                 {
24104                     tag: 'td',
24105                     cls: 'separator'
24106                 },
24107                 {
24108                     tag: 'td',
24109                     cn: [
24110                         {
24111                             tag: 'button',
24112                             type: 'button',
24113                             cls: 'btn btn-primary period',
24114                             html: 'AM'
24115                             
24116                         }
24117                     ]
24118                 }
24119             ]
24120         });
24121         
24122         time.createChild({
24123             tag: 'tr',
24124             cn: [
24125                 {
24126                     tag: 'td',
24127                     cn: [
24128                         {
24129                             tag: 'a',
24130                             href: '#',
24131                             cls: 'btn',
24132                             cn: [
24133                                 {
24134                                     tag: 'span',
24135                                     cls: 'hours-down fa fas fa-chevron-down'
24136                                 }
24137                             ]
24138                         }
24139                     ]
24140                 },
24141                 {
24142                     tag: 'td',
24143                     cls: 'separator'
24144                 },
24145                 {
24146                     tag: 'td',
24147                     cn: [
24148                         {
24149                             tag: 'a',
24150                             href: '#',
24151                             cls: 'btn',
24152                             cn: [
24153                                 {
24154                                     tag: 'span',
24155                                     cls: 'minutes-down fa fas fa-chevron-down'
24156                                 }
24157                             ]
24158                         }
24159                     ]
24160                 },
24161                 {
24162                     tag: 'td',
24163                     cls: 'separator'
24164                 }
24165             ]
24166         });
24167         
24168     },
24169     
24170     update: function()
24171     {
24172         
24173         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24174         
24175         this.fill();
24176     },
24177     
24178     fill: function() 
24179     {
24180         var hours = this.time.getHours();
24181         var minutes = this.time.getMinutes();
24182         var period = 'AM';
24183         
24184         if(hours > 11){
24185             period = 'PM';
24186         }
24187         
24188         if(hours == 0){
24189             hours = 12;
24190         }
24191         
24192         
24193         if(hours > 12){
24194             hours = hours - 12;
24195         }
24196         
24197         if(hours < 10){
24198             hours = '0' + hours;
24199         }
24200         
24201         if(minutes < 10){
24202             minutes = '0' + minutes;
24203         }
24204         
24205         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24206         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24207         this.pop.select('button', true).first().dom.innerHTML = period;
24208         
24209     },
24210     
24211     place: function()
24212     {   
24213         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24214         
24215         var cls = ['bottom'];
24216         
24217         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24218             cls.pop();
24219             cls.push('top');
24220         }
24221         
24222         cls.push('right');
24223         
24224         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24225             cls.pop();
24226             cls.push('left');
24227         }
24228         //this.picker().setXY(20000,20000);
24229         this.picker().addClass(cls.join('-'));
24230         
24231         var _this = this;
24232         
24233         Roo.each(cls, function(c){
24234             if(c == 'bottom'){
24235                 (function() {
24236                  //  
24237                 }).defer(200);
24238                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24239                 //_this.picker().setTop(_this.inputEl().getHeight());
24240                 return;
24241             }
24242             if(c == 'top'){
24243                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24244                 
24245                 //_this.picker().setTop(0 - _this.picker().getHeight());
24246                 return;
24247             }
24248             /*
24249             if(c == 'left'){
24250                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24251                 return;
24252             }
24253             if(c == 'right'){
24254                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24255                 return;
24256             }
24257             */
24258         });
24259         
24260     },
24261   
24262     onFocus : function()
24263     {
24264         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24265         this.show();
24266     },
24267     
24268     onBlur : function()
24269     {
24270         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24271         this.hide();
24272     },
24273     
24274     show : function()
24275     {
24276         this.picker().show();
24277         this.pop.show();
24278         this.update();
24279         this.place();
24280         
24281         this.fireEvent('show', this, this.date);
24282     },
24283     
24284     hide : function()
24285     {
24286         this.picker().hide();
24287         this.pop.hide();
24288         
24289         this.fireEvent('hide', this, this.date);
24290     },
24291     
24292     setTime : function()
24293     {
24294         this.hide();
24295         this.setValue(this.time.format(this.format));
24296         
24297         this.fireEvent('select', this, this.date);
24298         
24299         
24300     },
24301     
24302     onMousedown: function(e){
24303         e.stopPropagation();
24304         e.preventDefault();
24305     },
24306     
24307     onIncrementHours: function()
24308     {
24309         Roo.log('onIncrementHours');
24310         this.time = this.time.add(Date.HOUR, 1);
24311         this.update();
24312         
24313     },
24314     
24315     onDecrementHours: function()
24316     {
24317         Roo.log('onDecrementHours');
24318         this.time = this.time.add(Date.HOUR, -1);
24319         this.update();
24320     },
24321     
24322     onIncrementMinutes: function()
24323     {
24324         Roo.log('onIncrementMinutes');
24325         this.time = this.time.add(Date.MINUTE, 1);
24326         this.update();
24327     },
24328     
24329     onDecrementMinutes: function()
24330     {
24331         Roo.log('onDecrementMinutes');
24332         this.time = this.time.add(Date.MINUTE, -1);
24333         this.update();
24334     },
24335     
24336     onTogglePeriod: function()
24337     {
24338         Roo.log('onTogglePeriod');
24339         this.time = this.time.add(Date.HOUR, 12);
24340         this.update();
24341     }
24342     
24343    
24344 });
24345  
24346
24347 Roo.apply(Roo.bootstrap.form.TimeField,  {
24348   
24349     template : {
24350         tag: 'div',
24351         cls: 'datepicker dropdown-menu',
24352         cn: [
24353             {
24354                 tag: 'div',
24355                 cls: 'datepicker-time',
24356                 cn: [
24357                 {
24358                     tag: 'table',
24359                     cls: 'table-condensed',
24360                     cn:[
24361                         {
24362                             tag: 'tbody',
24363                             cn: [
24364                                 {
24365                                     tag: 'tr',
24366                                     cn: [
24367                                     {
24368                                         tag: 'td',
24369                                         colspan: '7'
24370                                     }
24371                                     ]
24372                                 }
24373                             ]
24374                         },
24375                         {
24376                             tag: 'tfoot',
24377                             cn: [
24378                                 {
24379                                     tag: 'tr',
24380                                     cn: [
24381                                     {
24382                                         tag: 'th',
24383                                         colspan: '7',
24384                                         cls: '',
24385                                         cn: [
24386                                             {
24387                                                 tag: 'button',
24388                                                 cls: 'btn btn-info ok',
24389                                                 html: 'OK'
24390                                             }
24391                                         ]
24392                                     }
24393                     
24394                                     ]
24395                                 }
24396                             ]
24397                         }
24398                     ]
24399                 }
24400                 ]
24401             }
24402         ]
24403     }
24404 });
24405
24406  
24407
24408  /*
24409  * - LGPL
24410  *
24411  * MonthField
24412  * 
24413  */
24414
24415 /**
24416  * @class Roo.bootstrap.form.MonthField
24417  * @extends Roo.bootstrap.form.Input
24418  * Bootstrap MonthField class
24419  * 
24420  * @cfg {String} language default en
24421  * 
24422  * @constructor
24423  * Create a new MonthField
24424  * @param {Object} config The config object
24425  */
24426
24427 Roo.bootstrap.form.MonthField = function(config){
24428     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24429     
24430     this.addEvents({
24431         /**
24432          * @event show
24433          * Fires when this field show.
24434          * @param {Roo.bootstrap.form.MonthField} this
24435          * @param {Mixed} date The date value
24436          */
24437         show : true,
24438         /**
24439          * @event show
24440          * Fires when this field hide.
24441          * @param {Roo.bootstrap.form.MonthField} this
24442          * @param {Mixed} date The date value
24443          */
24444         hide : true,
24445         /**
24446          * @event select
24447          * Fires when select a date.
24448          * @param {Roo.bootstrap.form.MonthField} this
24449          * @param {String} oldvalue The old value
24450          * @param {String} newvalue The new value
24451          */
24452         select : true
24453     });
24454 };
24455
24456 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24457     
24458     onRender: function(ct, position)
24459     {
24460         
24461         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24462         
24463         this.language = this.language || 'en';
24464         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24465         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24466         
24467         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24468         this.isInline = false;
24469         this.isInput = true;
24470         this.component = this.el.select('.add-on', true).first() || false;
24471         this.component = (this.component && this.component.length === 0) ? false : this.component;
24472         this.hasInput = this.component && this.inputEL().length;
24473         
24474         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24475         
24476         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24477         
24478         this.picker().on('mousedown', this.onMousedown, this);
24479         this.picker().on('click', this.onClick, this);
24480         
24481         this.picker().addClass('datepicker-dropdown');
24482         
24483         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24484             v.setStyle('width', '189px');
24485         });
24486         
24487         this.fillMonths();
24488         
24489         this.update();
24490         
24491         if(this.isInline) {
24492             this.show();
24493         }
24494         
24495     },
24496     
24497     setValue: function(v, suppressEvent)
24498     {   
24499         var o = this.getValue();
24500         
24501         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24502         
24503         this.update();
24504
24505         if(suppressEvent !== true){
24506             this.fireEvent('select', this, o, v);
24507         }
24508         
24509     },
24510     
24511     getValue: function()
24512     {
24513         return this.value;
24514     },
24515     
24516     onClick: function(e) 
24517     {
24518         e.stopPropagation();
24519         e.preventDefault();
24520         
24521         var target = e.getTarget();
24522         
24523         if(target.nodeName.toLowerCase() === 'i'){
24524             target = Roo.get(target).dom.parentNode;
24525         }
24526         
24527         var nodeName = target.nodeName;
24528         var className = target.className;
24529         var html = target.innerHTML;
24530         
24531         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24532             return;
24533         }
24534         
24535         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24536         
24537         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24538         
24539         this.hide();
24540                         
24541     },
24542     
24543     picker : function()
24544     {
24545         return this.pickerEl;
24546     },
24547     
24548     fillMonths: function()
24549     {    
24550         var i = 0;
24551         var months = this.picker().select('>.datepicker-months td', true).first();
24552         
24553         months.dom.innerHTML = '';
24554         
24555         while (i < 12) {
24556             var month = {
24557                 tag: 'span',
24558                 cls: 'month',
24559                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24560             };
24561             
24562             months.createChild(month);
24563         }
24564         
24565     },
24566     
24567     update: function()
24568     {
24569         var _this = this;
24570         
24571         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24572             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24573         }
24574         
24575         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24576             e.removeClass('active');
24577             
24578             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24579                 e.addClass('active');
24580             }
24581         })
24582     },
24583     
24584     place: function()
24585     {
24586         if(this.isInline) {
24587             return;
24588         }
24589         
24590         this.picker().removeClass(['bottom', 'top']);
24591         
24592         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24593             /*
24594              * place to the top of element!
24595              *
24596              */
24597             
24598             this.picker().addClass('top');
24599             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24600             
24601             return;
24602         }
24603         
24604         this.picker().addClass('bottom');
24605         
24606         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24607     },
24608     
24609     onFocus : function()
24610     {
24611         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24612         this.show();
24613     },
24614     
24615     onBlur : function()
24616     {
24617         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24618         
24619         var d = this.inputEl().getValue();
24620         
24621         this.setValue(d);
24622                 
24623         this.hide();
24624     },
24625     
24626     show : function()
24627     {
24628         this.picker().show();
24629         this.picker().select('>.datepicker-months', true).first().show();
24630         this.update();
24631         this.place();
24632         
24633         this.fireEvent('show', this, this.date);
24634     },
24635     
24636     hide : function()
24637     {
24638         if(this.isInline) {
24639             return;
24640         }
24641         this.picker().hide();
24642         this.fireEvent('hide', this, this.date);
24643         
24644     },
24645     
24646     onMousedown: function(e)
24647     {
24648         e.stopPropagation();
24649         e.preventDefault();
24650     },
24651     
24652     keyup: function(e)
24653     {
24654         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24655         this.update();
24656     },
24657
24658     fireKey: function(e)
24659     {
24660         if (!this.picker().isVisible()){
24661             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24662                 this.show();
24663             }
24664             return;
24665         }
24666         
24667         var dir;
24668         
24669         switch(e.keyCode){
24670             case 27: // escape
24671                 this.hide();
24672                 e.preventDefault();
24673                 break;
24674             case 37: // left
24675             case 39: // right
24676                 dir = e.keyCode == 37 ? -1 : 1;
24677                 
24678                 this.vIndex = this.vIndex + dir;
24679                 
24680                 if(this.vIndex < 0){
24681                     this.vIndex = 0;
24682                 }
24683                 
24684                 if(this.vIndex > 11){
24685                     this.vIndex = 11;
24686                 }
24687                 
24688                 if(isNaN(this.vIndex)){
24689                     this.vIndex = 0;
24690                 }
24691                 
24692                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24693                 
24694                 break;
24695             case 38: // up
24696             case 40: // down
24697                 
24698                 dir = e.keyCode == 38 ? -1 : 1;
24699                 
24700                 this.vIndex = this.vIndex + dir * 4;
24701                 
24702                 if(this.vIndex < 0){
24703                     this.vIndex = 0;
24704                 }
24705                 
24706                 if(this.vIndex > 11){
24707                     this.vIndex = 11;
24708                 }
24709                 
24710                 if(isNaN(this.vIndex)){
24711                     this.vIndex = 0;
24712                 }
24713                 
24714                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24715                 break;
24716                 
24717             case 13: // enter
24718                 
24719                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24720                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24721                 }
24722                 
24723                 this.hide();
24724                 e.preventDefault();
24725                 break;
24726             case 9: // tab
24727                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24728                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24729                 }
24730                 this.hide();
24731                 break;
24732             case 16: // shift
24733             case 17: // ctrl
24734             case 18: // alt
24735                 break;
24736             default :
24737                 this.hide();
24738                 
24739         }
24740     },
24741     
24742     remove: function() 
24743     {
24744         this.picker().remove();
24745     }
24746    
24747 });
24748
24749 Roo.apply(Roo.bootstrap.form.MonthField,  {
24750     
24751     content : {
24752         tag: 'tbody',
24753         cn: [
24754         {
24755             tag: 'tr',
24756             cn: [
24757             {
24758                 tag: 'td',
24759                 colspan: '7'
24760             }
24761             ]
24762         }
24763         ]
24764     },
24765     
24766     dates:{
24767         en: {
24768             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24769             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24770         }
24771     }
24772 });
24773
24774 Roo.apply(Roo.bootstrap.form.MonthField,  {
24775   
24776     template : {
24777         tag: 'div',
24778         cls: 'datepicker dropdown-menu roo-dynamic',
24779         cn: [
24780             {
24781                 tag: 'div',
24782                 cls: 'datepicker-months',
24783                 cn: [
24784                 {
24785                     tag: 'table',
24786                     cls: 'table-condensed',
24787                     cn:[
24788                         Roo.bootstrap.form.DateField.content
24789                     ]
24790                 }
24791                 ]
24792             }
24793         ]
24794     }
24795 });
24796
24797  
24798
24799  
24800  /*
24801  * - LGPL
24802  *
24803  * CheckBox
24804  * 
24805  */
24806
24807 /**
24808  * @class Roo.bootstrap.form.CheckBox
24809  * @extends Roo.bootstrap.form.Input
24810  * Bootstrap CheckBox class
24811  * 
24812  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24813  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24814  * @cfg {String} boxLabel The text that appears beside the checkbox
24815  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24816  * @cfg {Boolean} checked initnal the element
24817  * @cfg {Boolean} inline inline the element (default false)
24818  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24819  * @cfg {String} tooltip label tooltip
24820  * 
24821  * @constructor
24822  * Create a new CheckBox
24823  * @param {Object} config The config object
24824  */
24825
24826 Roo.bootstrap.form.CheckBox = function(config){
24827     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24828    
24829     this.addEvents({
24830         /**
24831         * @event check
24832         * Fires when the element is checked or unchecked.
24833         * @param {Roo.bootstrap.form.CheckBox} this This input
24834         * @param {Boolean} checked The new checked value
24835         */
24836        check : true,
24837        /**
24838         * @event click
24839         * Fires when the element is click.
24840         * @param {Roo.bootstrap.form.CheckBox} this This input
24841         */
24842        click : true
24843     });
24844     
24845 };
24846
24847 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24848   
24849     inputType: 'checkbox',
24850     inputValue: 1,
24851     valueOff: 0,
24852     boxLabel: false,
24853     checked: false,
24854     weight : false,
24855     inline: false,
24856     tooltip : '',
24857     
24858     // checkbox success does not make any sense really.. 
24859     invalidClass : "",
24860     validClass : "",
24861     
24862     
24863     getAutoCreate : function()
24864     {
24865         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24866         
24867         var id = Roo.id();
24868         
24869         var cfg = {};
24870         
24871         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24872         
24873         if(this.inline){
24874             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24875         }
24876         
24877         var input =  {
24878             tag: 'input',
24879             id : id,
24880             type : this.inputType,
24881             value : this.inputValue,
24882             cls : 'roo-' + this.inputType, //'form-box',
24883             placeholder : this.placeholder || ''
24884             
24885         };
24886         
24887         if(this.inputType != 'radio'){
24888             var hidden =  {
24889                 tag: 'input',
24890                 type : 'hidden',
24891                 cls : 'roo-hidden-value',
24892                 value : this.checked ? this.inputValue : this.valueOff
24893             };
24894         }
24895         
24896             
24897         if (this.weight) { // Validity check?
24898             cfg.cls += " " + this.inputType + "-" + this.weight;
24899         }
24900         
24901         if (this.disabled) {
24902             input.disabled=true;
24903         }
24904         
24905         if(this.checked){
24906             input.checked = this.checked;
24907         }
24908         
24909         if (this.name) {
24910             
24911             input.name = this.name;
24912             
24913             if(this.inputType != 'radio'){
24914                 hidden.name = this.name;
24915                 input.name = '_hidden_' + this.name;
24916             }
24917         }
24918         
24919         if (this.size) {
24920             input.cls += ' input-' + this.size;
24921         }
24922         
24923         var settings=this;
24924         
24925         ['xs','sm','md','lg'].map(function(size){
24926             if (settings[size]) {
24927                 cfg.cls += ' col-' + size + '-' + settings[size];
24928             }
24929         });
24930         
24931         var inputblock = input;
24932          
24933         if (this.before || this.after) {
24934             
24935             inputblock = {
24936                 cls : 'input-group',
24937                 cn :  [] 
24938             };
24939             
24940             if (this.before) {
24941                 inputblock.cn.push({
24942                     tag :'span',
24943                     cls : 'input-group-addon',
24944                     html : this.before
24945                 });
24946             }
24947             
24948             inputblock.cn.push(input);
24949             
24950             if(this.inputType != 'radio'){
24951                 inputblock.cn.push(hidden);
24952             }
24953             
24954             if (this.after) {
24955                 inputblock.cn.push({
24956                     tag :'span',
24957                     cls : 'input-group-addon',
24958                     html : this.after
24959                 });
24960             }
24961             
24962         }
24963         var boxLabelCfg = false;
24964         
24965         if(this.boxLabel){
24966            
24967             boxLabelCfg = {
24968                 tag: 'label',
24969                 //'for': id, // box label is handled by onclick - so no for...
24970                 cls: 'box-label',
24971                 html: this.boxLabel
24972             };
24973             if(this.tooltip){
24974                 boxLabelCfg.tooltip = this.tooltip;
24975             }
24976              
24977         }
24978         
24979         
24980         if (align ==='left' && this.fieldLabel.length) {
24981 //                Roo.log("left and has label");
24982             cfg.cn = [
24983                 {
24984                     tag: 'label',
24985                     'for' :  id,
24986                     cls : 'control-label',
24987                     html : this.fieldLabel
24988                 },
24989                 {
24990                     cls : "", 
24991                     cn: [
24992                         inputblock
24993                     ]
24994                 }
24995             ];
24996             
24997             if (boxLabelCfg) {
24998                 cfg.cn[1].cn.push(boxLabelCfg);
24999             }
25000             
25001             if(this.labelWidth > 12){
25002                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25003             }
25004             
25005             if(this.labelWidth < 13 && this.labelmd == 0){
25006                 this.labelmd = this.labelWidth;
25007             }
25008             
25009             if(this.labellg > 0){
25010                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25011                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25012             }
25013             
25014             if(this.labelmd > 0){
25015                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25016                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25017             }
25018             
25019             if(this.labelsm > 0){
25020                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25021                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25022             }
25023             
25024             if(this.labelxs > 0){
25025                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25026                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25027             }
25028             
25029         } else if ( this.fieldLabel.length) {
25030 //                Roo.log(" label");
25031                 cfg.cn = [
25032                    
25033                     {
25034                         tag: this.boxLabel ? 'span' : 'label',
25035                         'for': id,
25036                         cls: 'control-label box-input-label',
25037                         //cls : 'input-group-addon',
25038                         html : this.fieldLabel
25039                     },
25040                     
25041                     inputblock
25042                     
25043                 ];
25044                 if (boxLabelCfg) {
25045                     cfg.cn.push(boxLabelCfg);
25046                 }
25047
25048         } else {
25049             
25050 //                Roo.log(" no label && no align");
25051                 cfg.cn = [  inputblock ] ;
25052                 if (boxLabelCfg) {
25053                     cfg.cn.push(boxLabelCfg);
25054                 }
25055
25056                 
25057         }
25058         
25059        
25060         
25061         if(this.inputType != 'radio'){
25062             cfg.cn.push(hidden);
25063         }
25064         
25065         return cfg;
25066         
25067     },
25068     
25069     /**
25070      * return the real input element.
25071      */
25072     inputEl: function ()
25073     {
25074         return this.el.select('input.roo-' + this.inputType,true).first();
25075     },
25076     hiddenEl: function ()
25077     {
25078         return this.el.select('input.roo-hidden-value',true).first();
25079     },
25080     
25081     labelEl: function()
25082     {
25083         return this.el.select('label.control-label',true).first();
25084     },
25085     /* depricated... */
25086     
25087     label: function()
25088     {
25089         return this.labelEl();
25090     },
25091     
25092     boxLabelEl: function()
25093     {
25094         return this.el.select('label.box-label',true).first();
25095     },
25096     
25097     initEvents : function()
25098     {
25099 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25100         
25101         this.inputEl().on('click', this.onClick,  this);
25102         
25103         if (this.boxLabel) { 
25104             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25105         }
25106         
25107         this.startValue = this.getValue();
25108         
25109         if(this.groupId){
25110             Roo.bootstrap.form.CheckBox.register(this);
25111         }
25112     },
25113     
25114     onClick : function(e)
25115     {   
25116         if(this.fireEvent('click', this, e) !== false){
25117             this.setChecked(!this.checked);
25118         }
25119         
25120     },
25121     
25122     setChecked : function(state,suppressEvent)
25123     {
25124         this.startValue = this.getValue();
25125
25126         if(this.inputType == 'radio'){
25127             
25128             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25129                 e.dom.checked = false;
25130             });
25131             
25132             this.inputEl().dom.checked = true;
25133             
25134             this.inputEl().dom.value = this.inputValue;
25135             
25136             if(suppressEvent !== true){
25137                 this.fireEvent('check', this, true);
25138             }
25139             
25140             this.validate();
25141             
25142             return;
25143         }
25144         
25145         this.checked = state;
25146         
25147         this.inputEl().dom.checked = state;
25148         
25149         
25150         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25151         
25152         if(suppressEvent !== true){
25153             this.fireEvent('check', this, state);
25154         }
25155         
25156         this.validate();
25157     },
25158     
25159     getValue : function()
25160     {
25161         if(this.inputType == 'radio'){
25162             return this.getGroupValue();
25163         }
25164         
25165         return this.hiddenEl().dom.value;
25166         
25167     },
25168     
25169     getGroupValue : function()
25170     {
25171         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25172             return '';
25173         }
25174         
25175         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25176     },
25177     
25178     setValue : function(v,suppressEvent)
25179     {
25180         if(this.inputType == 'radio'){
25181             this.setGroupValue(v, suppressEvent);
25182             return;
25183         }
25184         
25185         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25186         
25187         this.validate();
25188     },
25189     
25190     setGroupValue : function(v, suppressEvent)
25191     {
25192         this.startValue = this.getValue();
25193         
25194         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25195             e.dom.checked = false;
25196             
25197             if(e.dom.value == v){
25198                 e.dom.checked = true;
25199             }
25200         });
25201         
25202         if(suppressEvent !== true){
25203             this.fireEvent('check', this, true);
25204         }
25205
25206         this.validate();
25207         
25208         return;
25209     },
25210     
25211     validate : function()
25212     {
25213         if(this.getVisibilityEl().hasClass('hidden')){
25214             return true;
25215         }
25216         
25217         if(
25218                 this.disabled || 
25219                 (this.inputType == 'radio' && this.validateRadio()) ||
25220                 (this.inputType == 'checkbox' && this.validateCheckbox())
25221         ){
25222             this.markValid();
25223             return true;
25224         }
25225         
25226         this.markInvalid();
25227         return false;
25228     },
25229     
25230     validateRadio : function()
25231     {
25232         if(this.getVisibilityEl().hasClass('hidden')){
25233             return true;
25234         }
25235         
25236         if(this.allowBlank){
25237             return true;
25238         }
25239         
25240         var valid = false;
25241         
25242         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25243             if(!e.dom.checked){
25244                 return;
25245             }
25246             
25247             valid = true;
25248             
25249             return false;
25250         });
25251         
25252         return valid;
25253     },
25254     
25255     validateCheckbox : function()
25256     {
25257         if(!this.groupId){
25258             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25259             //return (this.getValue() == this.inputValue) ? true : false;
25260         }
25261         
25262         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25263         
25264         if(!group){
25265             return false;
25266         }
25267         
25268         var r = false;
25269         
25270         for(var i in group){
25271             if(group[i].el.isVisible(true)){
25272                 r = false;
25273                 break;
25274             }
25275             
25276             r = true;
25277         }
25278         
25279         for(var i in group){
25280             if(r){
25281                 break;
25282             }
25283             
25284             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25285         }
25286         
25287         return r;
25288     },
25289     
25290     /**
25291      * Mark this field as valid
25292      */
25293     markValid : function()
25294     {
25295         var _this = this;
25296         
25297         this.fireEvent('valid', this);
25298         
25299         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25300         
25301         if(this.groupId){
25302             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25303         }
25304         
25305         if(label){
25306             label.markValid();
25307         }
25308
25309         if(this.inputType == 'radio'){
25310             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25311                 var fg = e.findParent('.form-group', false, true);
25312                 if (Roo.bootstrap.version == 3) {
25313                     fg.removeClass([_this.invalidClass, _this.validClass]);
25314                     fg.addClass(_this.validClass);
25315                 } else {
25316                     fg.removeClass(['is-valid', 'is-invalid']);
25317                     fg.addClass('is-valid');
25318                 }
25319             });
25320             
25321             return;
25322         }
25323
25324         if(!this.groupId){
25325             var fg = this.el.findParent('.form-group', false, true);
25326             if (Roo.bootstrap.version == 3) {
25327                 fg.removeClass([this.invalidClass, this.validClass]);
25328                 fg.addClass(this.validClass);
25329             } else {
25330                 fg.removeClass(['is-valid', 'is-invalid']);
25331                 fg.addClass('is-valid');
25332             }
25333             return;
25334         }
25335         
25336         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25337         
25338         if(!group){
25339             return;
25340         }
25341         
25342         for(var i in group){
25343             var fg = group[i].el.findParent('.form-group', false, true);
25344             if (Roo.bootstrap.version == 3) {
25345                 fg.removeClass([this.invalidClass, this.validClass]);
25346                 fg.addClass(this.validClass);
25347             } else {
25348                 fg.removeClass(['is-valid', 'is-invalid']);
25349                 fg.addClass('is-valid');
25350             }
25351         }
25352     },
25353     
25354      /**
25355      * Mark this field as invalid
25356      * @param {String} msg The validation message
25357      */
25358     markInvalid : function(msg)
25359     {
25360         if(this.allowBlank){
25361             return;
25362         }
25363         
25364         var _this = this;
25365         
25366         this.fireEvent('invalid', this, msg);
25367         
25368         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25369         
25370         if(this.groupId){
25371             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25372         }
25373         
25374         if(label){
25375             label.markInvalid();
25376         }
25377             
25378         if(this.inputType == 'radio'){
25379             
25380             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25381                 var fg = e.findParent('.form-group', false, true);
25382                 if (Roo.bootstrap.version == 3) {
25383                     fg.removeClass([_this.invalidClass, _this.validClass]);
25384                     fg.addClass(_this.invalidClass);
25385                 } else {
25386                     fg.removeClass(['is-invalid', 'is-valid']);
25387                     fg.addClass('is-invalid');
25388                 }
25389             });
25390             
25391             return;
25392         }
25393         
25394         if(!this.groupId){
25395             var fg = this.el.findParent('.form-group', false, true);
25396             if (Roo.bootstrap.version == 3) {
25397                 fg.removeClass([_this.invalidClass, _this.validClass]);
25398                 fg.addClass(_this.invalidClass);
25399             } else {
25400                 fg.removeClass(['is-invalid', 'is-valid']);
25401                 fg.addClass('is-invalid');
25402             }
25403             return;
25404         }
25405         
25406         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25407         
25408         if(!group){
25409             return;
25410         }
25411         
25412         for(var i in group){
25413             var fg = group[i].el.findParent('.form-group', false, true);
25414             if (Roo.bootstrap.version == 3) {
25415                 fg.removeClass([_this.invalidClass, _this.validClass]);
25416                 fg.addClass(_this.invalidClass);
25417             } else {
25418                 fg.removeClass(['is-invalid', 'is-valid']);
25419                 fg.addClass('is-invalid');
25420             }
25421         }
25422         
25423     },
25424     
25425     clearInvalid : function()
25426     {
25427         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25428         
25429         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25430         
25431         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25432         
25433         if (label && label.iconEl) {
25434             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25435             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25436         }
25437     },
25438     
25439     disable : function()
25440     {
25441         if(this.inputType != 'radio'){
25442             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25443             return;
25444         }
25445         
25446         var _this = this;
25447         
25448         if(this.rendered){
25449             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25450                 _this.getActionEl().addClass(this.disabledClass);
25451                 e.dom.disabled = true;
25452             });
25453         }
25454         
25455         this.disabled = true;
25456         this.fireEvent("disable", this);
25457         return this;
25458     },
25459
25460     enable : function()
25461     {
25462         if(this.inputType != 'radio'){
25463             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25464             return;
25465         }
25466         
25467         var _this = this;
25468         
25469         if(this.rendered){
25470             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25471                 _this.getActionEl().removeClass(this.disabledClass);
25472                 e.dom.disabled = false;
25473             });
25474         }
25475         
25476         this.disabled = false;
25477         this.fireEvent("enable", this);
25478         return this;
25479     },
25480     
25481     setBoxLabel : function(v)
25482     {
25483         this.boxLabel = v;
25484         
25485         if(this.rendered){
25486             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25487         }
25488     }
25489
25490 });
25491
25492 Roo.apply(Roo.bootstrap.form.CheckBox, {
25493     
25494     groups: {},
25495     
25496      /**
25497     * register a CheckBox Group
25498     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25499     */
25500     register : function(checkbox)
25501     {
25502         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25503             this.groups[checkbox.groupId] = {};
25504         }
25505         
25506         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25507             return;
25508         }
25509         
25510         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25511         
25512     },
25513     /**
25514     * fetch a CheckBox Group based on the group ID
25515     * @param {string} the group ID
25516     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25517     */
25518     get: function(groupId) {
25519         if (typeof(this.groups[groupId]) == 'undefined') {
25520             return false;
25521         }
25522         
25523         return this.groups[groupId] ;
25524     }
25525     
25526     
25527 });
25528 /*
25529  * - LGPL
25530  *
25531  * RadioItem
25532  * 
25533  */
25534
25535 /**
25536  * @class Roo.bootstrap.form.Radio
25537  * @extends Roo.bootstrap.Component
25538  * Bootstrap Radio class
25539  * @cfg {String} boxLabel - the label associated
25540  * @cfg {String} value - the value of radio
25541  * 
25542  * @constructor
25543  * Create a new Radio
25544  * @param {Object} config The config object
25545  */
25546 Roo.bootstrap.form.Radio = function(config){
25547     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25548     
25549 };
25550
25551 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25552     
25553     boxLabel : '',
25554     
25555     value : '',
25556     
25557     getAutoCreate : function()
25558     {
25559         var cfg = {
25560             tag : 'div',
25561             cls : 'form-group radio',
25562             cn : [
25563                 {
25564                     tag : 'label',
25565                     cls : 'box-label',
25566                     html : this.boxLabel
25567                 }
25568             ]
25569         };
25570         
25571         return cfg;
25572     },
25573     
25574     initEvents : function() 
25575     {
25576         this.parent().register(this);
25577         
25578         this.el.on('click', this.onClick, this);
25579         
25580     },
25581     
25582     onClick : function(e)
25583     {
25584         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25585             this.setChecked(true);
25586         }
25587     },
25588     
25589     setChecked : function(state, suppressEvent)
25590     {
25591         this.parent().setValue(this.value, suppressEvent);
25592         
25593     },
25594     
25595     setBoxLabel : function(v)
25596     {
25597         this.boxLabel = v;
25598         
25599         if(this.rendered){
25600             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25601         }
25602     }
25603     
25604 });
25605  
25606
25607  /*
25608  * - LGPL
25609  *
25610  * Input
25611  * 
25612  */
25613
25614 /**
25615  * @class Roo.bootstrap.form.SecurePass
25616  * @extends Roo.bootstrap.form.Input
25617  * Bootstrap SecurePass class
25618  *
25619  * 
25620  * @constructor
25621  * Create a new SecurePass
25622  * @param {Object} config The config object
25623  */
25624  
25625 Roo.bootstrap.form.SecurePass = function (config) {
25626     // these go here, so the translation tool can replace them..
25627     this.errors = {
25628         PwdEmpty: "Please type a password, and then retype it to confirm.",
25629         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25630         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25631         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25632         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25633         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25634         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25635         TooWeak: "Your password is Too Weak."
25636     },
25637     this.meterLabel = "Password strength:";
25638     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25639     this.meterClass = [
25640         "roo-password-meter-tooweak", 
25641         "roo-password-meter-weak", 
25642         "roo-password-meter-medium", 
25643         "roo-password-meter-strong", 
25644         "roo-password-meter-grey"
25645     ];
25646     
25647     this.errors = {};
25648     
25649     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25650 }
25651
25652 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25653     /**
25654      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25655      * {
25656      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25657      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25658      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25659      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25660      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25661      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25662      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25663      * })
25664      */
25665     // private
25666     
25667     meterWidth: 300,
25668     errorMsg :'',    
25669     errors: false,
25670     imageRoot: '/',
25671     /**
25672      * @cfg {String/Object} Label for the strength meter (defaults to
25673      * 'Password strength:')
25674      */
25675     // private
25676     meterLabel: '',
25677     /**
25678      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25679      * ['Weak', 'Medium', 'Strong'])
25680      */
25681     // private    
25682     pwdStrengths: false,    
25683     // private
25684     strength: 0,
25685     // private
25686     _lastPwd: null,
25687     // private
25688     kCapitalLetter: 0,
25689     kSmallLetter: 1,
25690     kDigit: 2,
25691     kPunctuation: 3,
25692     
25693     insecure: false,
25694     // private
25695     initEvents: function ()
25696     {
25697         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25698
25699         if (this.el.is('input[type=password]') && Roo.isSafari) {
25700             this.el.on('keydown', this.SafariOnKeyDown, this);
25701         }
25702
25703         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25704     },
25705     // private
25706     onRender: function (ct, position)
25707     {
25708         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25709         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25710         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25711
25712         this.trigger.createChild({
25713                    cn: [
25714                     {
25715                     //id: 'PwdMeter',
25716                     tag: 'div',
25717                     cls: 'roo-password-meter-grey col-xs-12',
25718                     style: {
25719                         //width: 0,
25720                         //width: this.meterWidth + 'px'                                                
25721                         }
25722                     },
25723                     {                            
25724                          cls: 'roo-password-meter-text'                          
25725                     }
25726                 ]            
25727         });
25728
25729          
25730         if (this.hideTrigger) {
25731             this.trigger.setDisplayed(false);
25732         }
25733         this.setSize(this.width || '', this.height || '');
25734     },
25735     // private
25736     onDestroy: function ()
25737     {
25738         if (this.trigger) {
25739             this.trigger.removeAllListeners();
25740             this.trigger.remove();
25741         }
25742         if (this.wrap) {
25743             this.wrap.remove();
25744         }
25745         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25746     },
25747     // private
25748     checkStrength: function ()
25749     {
25750         var pwd = this.inputEl().getValue();
25751         if (pwd == this._lastPwd) {
25752             return;
25753         }
25754
25755         var strength;
25756         if (this.ClientSideStrongPassword(pwd)) {
25757             strength = 3;
25758         } else if (this.ClientSideMediumPassword(pwd)) {
25759             strength = 2;
25760         } else if (this.ClientSideWeakPassword(pwd)) {
25761             strength = 1;
25762         } else {
25763             strength = 0;
25764         }
25765         
25766         Roo.log('strength1: ' + strength);
25767         
25768         //var pm = this.trigger.child('div/div/div').dom;
25769         var pm = this.trigger.child('div/div');
25770         pm.removeClass(this.meterClass);
25771         pm.addClass(this.meterClass[strength]);
25772                 
25773         
25774         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25775                 
25776         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25777         
25778         this._lastPwd = pwd;
25779     },
25780     reset: function ()
25781     {
25782         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25783         
25784         this._lastPwd = '';
25785         
25786         var pm = this.trigger.child('div/div');
25787         pm.removeClass(this.meterClass);
25788         pm.addClass('roo-password-meter-grey');        
25789         
25790         
25791         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25792         
25793         pt.innerHTML = '';
25794         this.inputEl().dom.type='password';
25795     },
25796     // private
25797     validateValue: function (value)
25798     {
25799         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25800             return false;
25801         }
25802         if (value.length == 0) {
25803             if (this.allowBlank) {
25804                 this.clearInvalid();
25805                 return true;
25806             }
25807
25808             this.markInvalid(this.errors.PwdEmpty);
25809             this.errorMsg = this.errors.PwdEmpty;
25810             return false;
25811         }
25812         
25813         if(this.insecure){
25814             return true;
25815         }
25816         
25817         if (!value.match(/[\x21-\x7e]+/)) {
25818             this.markInvalid(this.errors.PwdBadChar);
25819             this.errorMsg = this.errors.PwdBadChar;
25820             return false;
25821         }
25822         if (value.length < 6) {
25823             this.markInvalid(this.errors.PwdShort);
25824             this.errorMsg = this.errors.PwdShort;
25825             return false;
25826         }
25827         if (value.length > 16) {
25828             this.markInvalid(this.errors.PwdLong);
25829             this.errorMsg = this.errors.PwdLong;
25830             return false;
25831         }
25832         var strength;
25833         if (this.ClientSideStrongPassword(value)) {
25834             strength = 3;
25835         } else if (this.ClientSideMediumPassword(value)) {
25836             strength = 2;
25837         } else if (this.ClientSideWeakPassword(value)) {
25838             strength = 1;
25839         } else {
25840             strength = 0;
25841         }
25842
25843         
25844         if (strength < 2) {
25845             //this.markInvalid(this.errors.TooWeak);
25846             this.errorMsg = this.errors.TooWeak;
25847             //return false;
25848         }
25849         
25850         
25851         console.log('strength2: ' + strength);
25852         
25853         //var pm = this.trigger.child('div/div/div').dom;
25854         
25855         var pm = this.trigger.child('div/div');
25856         pm.removeClass(this.meterClass);
25857         pm.addClass(this.meterClass[strength]);
25858                 
25859         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25860                 
25861         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25862         
25863         this.errorMsg = ''; 
25864         return true;
25865     },
25866     // private
25867     CharacterSetChecks: function (type)
25868     {
25869         this.type = type;
25870         this.fResult = false;
25871     },
25872     // private
25873     isctype: function (character, type)
25874     {
25875         switch (type) {  
25876             case this.kCapitalLetter:
25877                 if (character >= 'A' && character <= 'Z') {
25878                     return true;
25879                 }
25880                 break;
25881             
25882             case this.kSmallLetter:
25883                 if (character >= 'a' && character <= 'z') {
25884                     return true;
25885                 }
25886                 break;
25887             
25888             case this.kDigit:
25889                 if (character >= '0' && character <= '9') {
25890                     return true;
25891                 }
25892                 break;
25893             
25894             case this.kPunctuation:
25895                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25896                     return true;
25897                 }
25898                 break;
25899             
25900             default:
25901                 return false;
25902         }
25903
25904     },
25905     // private
25906     IsLongEnough: function (pwd, size)
25907     {
25908         return !(pwd == null || isNaN(size) || pwd.length < size);
25909     },
25910     // private
25911     SpansEnoughCharacterSets: function (word, nb)
25912     {
25913         if (!this.IsLongEnough(word, nb))
25914         {
25915             return false;
25916         }
25917
25918         var characterSetChecks = new Array(
25919             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25920             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25921         );
25922         
25923         for (var index = 0; index < word.length; ++index) {
25924             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25925                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25926                     characterSetChecks[nCharSet].fResult = true;
25927                     break;
25928                 }
25929             }
25930         }
25931
25932         var nCharSets = 0;
25933         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25934             if (characterSetChecks[nCharSet].fResult) {
25935                 ++nCharSets;
25936             }
25937         }
25938
25939         if (nCharSets < nb) {
25940             return false;
25941         }
25942         return true;
25943     },
25944     // private
25945     ClientSideStrongPassword: function (pwd)
25946     {
25947         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25948     },
25949     // private
25950     ClientSideMediumPassword: function (pwd)
25951     {
25952         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25953     },
25954     // private
25955     ClientSideWeakPassword: function (pwd)
25956     {
25957         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25958     }
25959           
25960 })//<script type="text/javascript">
25961
25962 /*
25963  * Based  Ext JS Library 1.1.1
25964  * Copyright(c) 2006-2007, Ext JS, LLC.
25965  * LGPL
25966  *
25967  */
25968  
25969 /**
25970  * @class Roo.HtmlEditorCore
25971  * @extends Roo.Component
25972  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25973  *
25974  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25975  */
25976
25977 Roo.HtmlEditorCore = function(config){
25978     
25979     
25980     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25981     
25982     
25983     this.addEvents({
25984         /**
25985          * @event initialize
25986          * Fires when the editor is fully initialized (including the iframe)
25987          * @param {Roo.HtmlEditorCore} this
25988          */
25989         initialize: true,
25990         /**
25991          * @event activate
25992          * Fires when the editor is first receives the focus. Any insertion must wait
25993          * until after this event.
25994          * @param {Roo.HtmlEditorCore} this
25995          */
25996         activate: true,
25997          /**
25998          * @event beforesync
25999          * Fires before the textarea is updated with content from the editor iframe. Return false
26000          * to cancel the sync.
26001          * @param {Roo.HtmlEditorCore} this
26002          * @param {String} html
26003          */
26004         beforesync: true,
26005          /**
26006          * @event beforepush
26007          * Fires before the iframe editor is updated with content from the textarea. Return false
26008          * to cancel the push.
26009          * @param {Roo.HtmlEditorCore} this
26010          * @param {String} html
26011          */
26012         beforepush: true,
26013          /**
26014          * @event sync
26015          * Fires when the textarea is updated with content from the editor iframe.
26016          * @param {Roo.HtmlEditorCore} this
26017          * @param {String} html
26018          */
26019         sync: true,
26020          /**
26021          * @event push
26022          * Fires when the iframe editor is updated with content from the textarea.
26023          * @param {Roo.HtmlEditorCore} this
26024          * @param {String} html
26025          */
26026         push: true,
26027         
26028         /**
26029          * @event editorevent
26030          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26031          * @param {Roo.HtmlEditorCore} this
26032          */
26033         editorevent: true
26034         
26035     });
26036     
26037     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26038     
26039     // defaults : white / black...
26040     this.applyBlacklists();
26041     
26042     
26043     
26044 };
26045
26046
26047 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
26048
26049
26050      /**
26051      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
26052      */
26053     
26054     owner : false,
26055     
26056      /**
26057      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26058      *                        Roo.resizable.
26059      */
26060     resizable : false,
26061      /**
26062      * @cfg {Number} height (in pixels)
26063      */   
26064     height: 300,
26065    /**
26066      * @cfg {Number} width (in pixels)
26067      */   
26068     width: 500,
26069     
26070     /**
26071      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26072      * 
26073      */
26074     stylesheets: false,
26075     
26076     /**
26077      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26078      */
26079     allowComments: false,
26080     // id of frame..
26081     frameId: false,
26082     
26083     // private properties
26084     validationEvent : false,
26085     deferHeight: true,
26086     initialized : false,
26087     activated : false,
26088     sourceEditMode : false,
26089     onFocus : Roo.emptyFn,
26090     iframePad:3,
26091     hideMode:'offsets',
26092     
26093     clearUp: true,
26094     
26095     // blacklist + whitelisted elements..
26096     black: false,
26097     white: false,
26098      
26099     bodyCls : '',
26100
26101     /**
26102      * Protected method that will not generally be called directly. It
26103      * is called when the editor initializes the iframe with HTML contents. Override this method if you
26104      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26105      */
26106     getDocMarkup : function(){
26107         // body styles..
26108         var st = '';
26109         
26110         // inherit styels from page...?? 
26111         if (this.stylesheets === false) {
26112             
26113             Roo.get(document.head).select('style').each(function(node) {
26114                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26115             });
26116             
26117             Roo.get(document.head).select('link').each(function(node) { 
26118                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26119             });
26120             
26121         } else if (!this.stylesheets.length) {
26122                 // simple..
26123                 st = '<style type="text/css">' +
26124                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26125                    '</style>';
26126         } else {
26127             for (var i in this.stylesheets) { 
26128                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26129             }
26130             
26131         }
26132         
26133         st +=  '<style type="text/css">' +
26134             'IMG { cursor: pointer } ' +
26135         '</style>';
26136
26137         var cls = 'roo-htmleditor-body';
26138         
26139         if(this.bodyCls.length){
26140             cls += ' ' + this.bodyCls;
26141         }
26142         
26143         return '<html><head>' + st  +
26144             //<style type="text/css">' +
26145             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26146             //'</style>' +
26147             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
26148     },
26149
26150     // private
26151     onRender : function(ct, position)
26152     {
26153         var _t = this;
26154         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26155         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26156         
26157         
26158         this.el.dom.style.border = '0 none';
26159         this.el.dom.setAttribute('tabIndex', -1);
26160         this.el.addClass('x-hidden hide');
26161         
26162         
26163         
26164         if(Roo.isIE){ // fix IE 1px bogus margin
26165             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26166         }
26167        
26168         
26169         this.frameId = Roo.id();
26170         
26171          
26172         
26173         var iframe = this.owner.wrap.createChild({
26174             tag: 'iframe',
26175             cls: 'form-control', // bootstrap..
26176             id: this.frameId,
26177             name: this.frameId,
26178             frameBorder : 'no',
26179             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
26180         }, this.el
26181         );
26182         
26183         
26184         this.iframe = iframe.dom;
26185
26186          this.assignDocWin();
26187         
26188         this.doc.designMode = 'on';
26189        
26190         this.doc.open();
26191         this.doc.write(this.getDocMarkup());
26192         this.doc.close();
26193
26194         
26195         var task = { // must defer to wait for browser to be ready
26196             run : function(){
26197                 //console.log("run task?" + this.doc.readyState);
26198                 this.assignDocWin();
26199                 if(this.doc.body || this.doc.readyState == 'complete'){
26200                     try {
26201                         this.doc.designMode="on";
26202                     } catch (e) {
26203                         return;
26204                     }
26205                     Roo.TaskMgr.stop(task);
26206                     this.initEditor.defer(10, this);
26207                 }
26208             },
26209             interval : 10,
26210             duration: 10000,
26211             scope: this
26212         };
26213         Roo.TaskMgr.start(task);
26214
26215     },
26216
26217     // private
26218     onResize : function(w, h)
26219     {
26220          Roo.log('resize: ' +w + ',' + h );
26221         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26222         if(!this.iframe){
26223             return;
26224         }
26225         if(typeof w == 'number'){
26226             
26227             this.iframe.style.width = w + 'px';
26228         }
26229         if(typeof h == 'number'){
26230             
26231             this.iframe.style.height = h + 'px';
26232             if(this.doc){
26233                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26234             }
26235         }
26236         
26237     },
26238
26239     /**
26240      * Toggles the editor between standard and source edit mode.
26241      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26242      */
26243     toggleSourceEdit : function(sourceEditMode){
26244         
26245         this.sourceEditMode = sourceEditMode === true;
26246         
26247         if(this.sourceEditMode){
26248  
26249             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
26250             
26251         }else{
26252             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26253             //this.iframe.className = '';
26254             this.deferFocus();
26255         }
26256         //this.setSize(this.owner.wrap.getSize());
26257         //this.fireEvent('editmodechange', this, this.sourceEditMode);
26258     },
26259
26260     
26261   
26262
26263     /**
26264      * Protected method that will not generally be called directly. If you need/want
26265      * custom HTML cleanup, this is the method you should override.
26266      * @param {String} html The HTML to be cleaned
26267      * return {String} The cleaned HTML
26268      */
26269     cleanHtml : function(html){
26270         html = String(html);
26271         if(html.length > 5){
26272             if(Roo.isSafari){ // strip safari nonsense
26273                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26274             }
26275         }
26276         if(html == '&nbsp;'){
26277             html = '';
26278         }
26279         return html;
26280     },
26281
26282     /**
26283      * HTML Editor -> Textarea
26284      * Protected method that will not generally be called directly. Syncs the contents
26285      * of the editor iframe with the textarea.
26286      */
26287     syncValue : function(){
26288         if(this.initialized){
26289             var bd = (this.doc.body || this.doc.documentElement);
26290             //this.cleanUpPaste(); -- this is done else where and causes havoc..
26291             var html = bd.innerHTML;
26292             if(Roo.isSafari){
26293                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26294                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26295                 if(m && m[1]){
26296                     html = '<div style="'+m[0]+'">' + html + '</div>';
26297                 }
26298             }
26299             html = this.cleanHtml(html);
26300             // fix up the special chars.. normaly like back quotes in word...
26301             // however we do not want to do this with chinese..
26302             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26303                 
26304                 var cc = match.charCodeAt();
26305
26306                 // Get the character value, handling surrogate pairs
26307                 if (match.length == 2) {
26308                     // It's a surrogate pair, calculate the Unicode code point
26309                     var high = match.charCodeAt(0) - 0xD800;
26310                     var low  = match.charCodeAt(1) - 0xDC00;
26311                     cc = (high * 0x400) + low + 0x10000;
26312                 }  else if (
26313                     (cc >= 0x4E00 && cc < 0xA000 ) ||
26314                     (cc >= 0x3400 && cc < 0x4E00 ) ||
26315                     (cc >= 0xf900 && cc < 0xfb00 )
26316                 ) {
26317                         return match;
26318                 }  
26319          
26320                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26321                 return "&#" + cc + ";";
26322                 
26323                 
26324             });
26325             
26326             
26327              
26328             if(this.owner.fireEvent('beforesync', this, html) !== false){
26329                 this.el.dom.value = html;
26330                 this.owner.fireEvent('sync', this, html);
26331             }
26332         }
26333     },
26334
26335     /**
26336      * Protected method that will not generally be called directly. Pushes the value of the textarea
26337      * into the iframe editor.
26338      */
26339     pushValue : function(){
26340         if(this.initialized){
26341             var v = this.el.dom.value.trim();
26342             
26343 //            if(v.length < 1){
26344 //                v = '&#160;';
26345 //            }
26346             
26347             if(this.owner.fireEvent('beforepush', this, v) !== false){
26348                 var d = (this.doc.body || this.doc.documentElement);
26349                 d.innerHTML = v;
26350                 this.cleanUpPaste();
26351                 this.el.dom.value = d.innerHTML;
26352                 this.owner.fireEvent('push', this, v);
26353             }
26354         }
26355     },
26356
26357     // private
26358     deferFocus : function(){
26359         this.focus.defer(10, this);
26360     },
26361
26362     // doc'ed in Field
26363     focus : function(){
26364         if(this.win && !this.sourceEditMode){
26365             this.win.focus();
26366         }else{
26367             this.el.focus();
26368         }
26369     },
26370     
26371     assignDocWin: function()
26372     {
26373         var iframe = this.iframe;
26374         
26375          if(Roo.isIE){
26376             this.doc = iframe.contentWindow.document;
26377             this.win = iframe.contentWindow;
26378         } else {
26379 //            if (!Roo.get(this.frameId)) {
26380 //                return;
26381 //            }
26382 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26383 //            this.win = Roo.get(this.frameId).dom.contentWindow;
26384             
26385             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26386                 return;
26387             }
26388             
26389             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26390             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26391         }
26392     },
26393     
26394     // private
26395     initEditor : function(){
26396         //console.log("INIT EDITOR");
26397         this.assignDocWin();
26398         
26399         
26400         
26401         this.doc.designMode="on";
26402         this.doc.open();
26403         this.doc.write(this.getDocMarkup());
26404         this.doc.close();
26405         
26406         var dbody = (this.doc.body || this.doc.documentElement);
26407         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26408         // this copies styles from the containing element into thsi one..
26409         // not sure why we need all of this..
26410         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26411         
26412         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26413         //ss['background-attachment'] = 'fixed'; // w3c
26414         dbody.bgProperties = 'fixed'; // ie
26415         //Roo.DomHelper.applyStyles(dbody, ss);
26416         Roo.EventManager.on(this.doc, {
26417             //'mousedown': this.onEditorEvent,
26418             'mouseup': this.onEditorEvent,
26419             'dblclick': this.onEditorEvent,
26420             'click': this.onEditorEvent,
26421             'keyup': this.onEditorEvent,
26422             buffer:100,
26423             scope: this
26424         });
26425         if(Roo.isGecko){
26426             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26427         }
26428         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26429             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26430         }
26431         this.initialized = true;
26432
26433         this.owner.fireEvent('initialize', this);
26434         this.pushValue();
26435     },
26436
26437     // private
26438     onDestroy : function(){
26439         
26440         
26441         
26442         if(this.rendered){
26443             
26444             //for (var i =0; i < this.toolbars.length;i++) {
26445             //    // fixme - ask toolbars for heights?
26446             //    this.toolbars[i].onDestroy();
26447            // }
26448             
26449             //this.wrap.dom.innerHTML = '';
26450             //this.wrap.remove();
26451         }
26452     },
26453
26454     // private
26455     onFirstFocus : function(){
26456         
26457         this.assignDocWin();
26458         
26459         
26460         this.activated = true;
26461          
26462     
26463         if(Roo.isGecko){ // prevent silly gecko errors
26464             this.win.focus();
26465             var s = this.win.getSelection();
26466             if(!s.focusNode || s.focusNode.nodeType != 3){
26467                 var r = s.getRangeAt(0);
26468                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26469                 r.collapse(true);
26470                 this.deferFocus();
26471             }
26472             try{
26473                 this.execCmd('useCSS', true);
26474                 this.execCmd('styleWithCSS', false);
26475             }catch(e){}
26476         }
26477         this.owner.fireEvent('activate', this);
26478     },
26479
26480     // private
26481     adjustFont: function(btn){
26482         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26483         //if(Roo.isSafari){ // safari
26484         //    adjust *= 2;
26485        // }
26486         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26487         if(Roo.isSafari){ // safari
26488             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26489             v =  (v < 10) ? 10 : v;
26490             v =  (v > 48) ? 48 : v;
26491             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26492             
26493         }
26494         
26495         
26496         v = Math.max(1, v+adjust);
26497         
26498         this.execCmd('FontSize', v  );
26499     },
26500
26501     onEditorEvent : function(e)
26502     {
26503         this.owner.fireEvent('editorevent', this, e);
26504       //  this.updateToolbar();
26505         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26506     },
26507
26508     insertTag : function(tg)
26509     {
26510         // could be a bit smarter... -> wrap the current selected tRoo..
26511         if (tg.toLowerCase() == 'span' ||
26512             tg.toLowerCase() == 'code' ||
26513             tg.toLowerCase() == 'sup' ||
26514             tg.toLowerCase() == 'sub' 
26515             ) {
26516             
26517             range = this.createRange(this.getSelection());
26518             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26519             wrappingNode.appendChild(range.extractContents());
26520             range.insertNode(wrappingNode);
26521
26522             return;
26523             
26524             
26525             
26526         }
26527         this.execCmd("formatblock",   tg);
26528         
26529     },
26530     
26531     insertText : function(txt)
26532     {
26533         
26534         
26535         var range = this.createRange();
26536         range.deleteContents();
26537                //alert(Sender.getAttribute('label'));
26538                
26539         range.insertNode(this.doc.createTextNode(txt));
26540     } ,
26541     
26542      
26543
26544     /**
26545      * Executes a Midas editor command on the editor document and performs necessary focus and
26546      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26547      * @param {String} cmd The Midas command
26548      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26549      */
26550     relayCmd : function(cmd, value){
26551         this.win.focus();
26552         this.execCmd(cmd, value);
26553         this.owner.fireEvent('editorevent', this);
26554         //this.updateToolbar();
26555         this.owner.deferFocus();
26556     },
26557
26558     /**
26559      * Executes a Midas editor command directly on the editor document.
26560      * For visual commands, you should use {@link #relayCmd} instead.
26561      * <b>This should only be called after the editor is initialized.</b>
26562      * @param {String} cmd The Midas command
26563      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26564      */
26565     execCmd : function(cmd, value){
26566         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26567         this.syncValue();
26568     },
26569  
26570  
26571    
26572     /**
26573      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26574      * to insert tRoo.
26575      * @param {String} text | dom node.. 
26576      */
26577     insertAtCursor : function(text)
26578     {
26579         
26580         if(!this.activated){
26581             return;
26582         }
26583         /*
26584         if(Roo.isIE){
26585             this.win.focus();
26586             var r = this.doc.selection.createRange();
26587             if(r){
26588                 r.collapse(true);
26589                 r.pasteHTML(text);
26590                 this.syncValue();
26591                 this.deferFocus();
26592             
26593             }
26594             return;
26595         }
26596         */
26597         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26598             this.win.focus();
26599             
26600             
26601             // from jquery ui (MIT licenced)
26602             var range, node;
26603             var win = this.win;
26604             
26605             if (win.getSelection && win.getSelection().getRangeAt) {
26606                 range = win.getSelection().getRangeAt(0);
26607                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26608                 range.insertNode(node);
26609             } else if (win.document.selection && win.document.selection.createRange) {
26610                 // no firefox support
26611                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26612                 win.document.selection.createRange().pasteHTML(txt);
26613             } else {
26614                 // no firefox support
26615                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26616                 this.execCmd('InsertHTML', txt);
26617             } 
26618             
26619             this.syncValue();
26620             
26621             this.deferFocus();
26622         }
26623     },
26624  // private
26625     mozKeyPress : function(e){
26626         if(e.ctrlKey){
26627             var c = e.getCharCode(), cmd;
26628           
26629             if(c > 0){
26630                 c = String.fromCharCode(c).toLowerCase();
26631                 switch(c){
26632                     case 'b':
26633                         cmd = 'bold';
26634                         break;
26635                     case 'i':
26636                         cmd = 'italic';
26637                         break;
26638                     
26639                     case 'u':
26640                         cmd = 'underline';
26641                         break;
26642                     
26643                     case 'v':
26644                         this.cleanUpPaste.defer(100, this);
26645                         return;
26646                         
26647                 }
26648                 if(cmd){
26649                     this.win.focus();
26650                     this.execCmd(cmd);
26651                     this.deferFocus();
26652                     e.preventDefault();
26653                 }
26654                 
26655             }
26656         }
26657     },
26658
26659     // private
26660     fixKeys : function(){ // load time branching for fastest keydown performance
26661         if(Roo.isIE){
26662             return function(e){
26663                 var k = e.getKey(), r;
26664                 if(k == e.TAB){
26665                     e.stopEvent();
26666                     r = this.doc.selection.createRange();
26667                     if(r){
26668                         r.collapse(true);
26669                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26670                         this.deferFocus();
26671                     }
26672                     return;
26673                 }
26674                 
26675                 if(k == e.ENTER){
26676                     r = this.doc.selection.createRange();
26677                     if(r){
26678                         var target = r.parentElement();
26679                         if(!target || target.tagName.toLowerCase() != 'li'){
26680                             e.stopEvent();
26681                             r.pasteHTML('<br />');
26682                             r.collapse(false);
26683                             r.select();
26684                         }
26685                     }
26686                 }
26687                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26688                     this.cleanUpPaste.defer(100, this);
26689                     return;
26690                 }
26691                 
26692                 
26693             };
26694         }else if(Roo.isOpera){
26695             return function(e){
26696                 var k = e.getKey();
26697                 if(k == e.TAB){
26698                     e.stopEvent();
26699                     this.win.focus();
26700                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26701                     this.deferFocus();
26702                 }
26703                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26704                     this.cleanUpPaste.defer(100, this);
26705                     return;
26706                 }
26707                 
26708             };
26709         }else if(Roo.isSafari){
26710             return function(e){
26711                 var k = e.getKey();
26712                 
26713                 if(k == e.TAB){
26714                     e.stopEvent();
26715                     this.execCmd('InsertText','\t');
26716                     this.deferFocus();
26717                     return;
26718                 }
26719                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26720                     this.cleanUpPaste.defer(100, this);
26721                     return;
26722                 }
26723                 
26724              };
26725         }
26726     }(),
26727     
26728     getAllAncestors: function()
26729     {
26730         var p = this.getSelectedNode();
26731         var a = [];
26732         if (!p) {
26733             a.push(p); // push blank onto stack..
26734             p = this.getParentElement();
26735         }
26736         
26737         
26738         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26739             a.push(p);
26740             p = p.parentNode;
26741         }
26742         a.push(this.doc.body);
26743         return a;
26744     },
26745     lastSel : false,
26746     lastSelNode : false,
26747     
26748     
26749     getSelection : function() 
26750     {
26751         this.assignDocWin();
26752         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26753     },
26754     
26755     getSelectedNode: function() 
26756     {
26757         // this may only work on Gecko!!!
26758         
26759         // should we cache this!!!!
26760         
26761         
26762         
26763          
26764         var range = this.createRange(this.getSelection()).cloneRange();
26765         
26766         if (Roo.isIE) {
26767             var parent = range.parentElement();
26768             while (true) {
26769                 var testRange = range.duplicate();
26770                 testRange.moveToElementText(parent);
26771                 if (testRange.inRange(range)) {
26772                     break;
26773                 }
26774                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26775                     break;
26776                 }
26777                 parent = parent.parentElement;
26778             }
26779             return parent;
26780         }
26781         
26782         // is ancestor a text element.
26783         var ac =  range.commonAncestorContainer;
26784         if (ac.nodeType == 3) {
26785             ac = ac.parentNode;
26786         }
26787         
26788         var ar = ac.childNodes;
26789          
26790         var nodes = [];
26791         var other_nodes = [];
26792         var has_other_nodes = false;
26793         for (var i=0;i<ar.length;i++) {
26794             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26795                 continue;
26796             }
26797             // fullly contained node.
26798             
26799             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26800                 nodes.push(ar[i]);
26801                 continue;
26802             }
26803             
26804             // probably selected..
26805             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26806                 other_nodes.push(ar[i]);
26807                 continue;
26808             }
26809             // outer..
26810             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26811                 continue;
26812             }
26813             
26814             
26815             has_other_nodes = true;
26816         }
26817         if (!nodes.length && other_nodes.length) {
26818             nodes= other_nodes;
26819         }
26820         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26821             return false;
26822         }
26823         
26824         return nodes[0];
26825     },
26826     createRange: function(sel)
26827     {
26828         // this has strange effects when using with 
26829         // top toolbar - not sure if it's a great idea.
26830         //this.editor.contentWindow.focus();
26831         if (typeof sel != "undefined") {
26832             try {
26833                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26834             } catch(e) {
26835                 return this.doc.createRange();
26836             }
26837         } else {
26838             return this.doc.createRange();
26839         }
26840     },
26841     getParentElement: function()
26842     {
26843         
26844         this.assignDocWin();
26845         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26846         
26847         var range = this.createRange(sel);
26848          
26849         try {
26850             var p = range.commonAncestorContainer;
26851             while (p.nodeType == 3) { // text node
26852                 p = p.parentNode;
26853             }
26854             return p;
26855         } catch (e) {
26856             return null;
26857         }
26858     
26859     },
26860     /***
26861      *
26862      * Range intersection.. the hard stuff...
26863      *  '-1' = before
26864      *  '0' = hits..
26865      *  '1' = after.
26866      *         [ -- selected range --- ]
26867      *   [fail]                        [fail]
26868      *
26869      *    basically..
26870      *      if end is before start or  hits it. fail.
26871      *      if start is after end or hits it fail.
26872      *
26873      *   if either hits (but other is outside. - then it's not 
26874      *   
26875      *    
26876      **/
26877     
26878     
26879     // @see http://www.thismuchiknow.co.uk/?p=64.
26880     rangeIntersectsNode : function(range, node)
26881     {
26882         var nodeRange = node.ownerDocument.createRange();
26883         try {
26884             nodeRange.selectNode(node);
26885         } catch (e) {
26886             nodeRange.selectNodeContents(node);
26887         }
26888     
26889         var rangeStartRange = range.cloneRange();
26890         rangeStartRange.collapse(true);
26891     
26892         var rangeEndRange = range.cloneRange();
26893         rangeEndRange.collapse(false);
26894     
26895         var nodeStartRange = nodeRange.cloneRange();
26896         nodeStartRange.collapse(true);
26897     
26898         var nodeEndRange = nodeRange.cloneRange();
26899         nodeEndRange.collapse(false);
26900     
26901         return rangeStartRange.compareBoundaryPoints(
26902                  Range.START_TO_START, nodeEndRange) == -1 &&
26903                rangeEndRange.compareBoundaryPoints(
26904                  Range.START_TO_START, nodeStartRange) == 1;
26905         
26906          
26907     },
26908     rangeCompareNode : function(range, node)
26909     {
26910         var nodeRange = node.ownerDocument.createRange();
26911         try {
26912             nodeRange.selectNode(node);
26913         } catch (e) {
26914             nodeRange.selectNodeContents(node);
26915         }
26916         
26917         
26918         range.collapse(true);
26919     
26920         nodeRange.collapse(true);
26921      
26922         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26923         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26924          
26925         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26926         
26927         var nodeIsBefore   =  ss == 1;
26928         var nodeIsAfter    = ee == -1;
26929         
26930         if (nodeIsBefore && nodeIsAfter) {
26931             return 0; // outer
26932         }
26933         if (!nodeIsBefore && nodeIsAfter) {
26934             return 1; //right trailed.
26935         }
26936         
26937         if (nodeIsBefore && !nodeIsAfter) {
26938             return 2;  // left trailed.
26939         }
26940         // fully contined.
26941         return 3;
26942     },
26943
26944     // private? - in a new class?
26945     cleanUpPaste :  function()
26946     {
26947         // cleans up the whole document..
26948         Roo.log('cleanuppaste');
26949         
26950         this.cleanUpChildren(this.doc.body);
26951         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26952         if (clean != this.doc.body.innerHTML) {
26953             this.doc.body.innerHTML = clean;
26954         }
26955         
26956     },
26957     
26958     cleanWordChars : function(input) {// change the chars to hex code
26959         var he = Roo.HtmlEditorCore;
26960         
26961         var output = input;
26962         Roo.each(he.swapCodes, function(sw) { 
26963             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26964             
26965             output = output.replace(swapper, sw[1]);
26966         });
26967         
26968         return output;
26969     },
26970     
26971     
26972     cleanUpChildren : function (n)
26973     {
26974         if (!n.childNodes.length) {
26975             return;
26976         }
26977         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26978            this.cleanUpChild(n.childNodes[i]);
26979         }
26980     },
26981     
26982     
26983         
26984     
26985     cleanUpChild : function (node)
26986     {
26987         var ed = this;
26988         //console.log(node);
26989         if (node.nodeName == "#text") {
26990             // clean up silly Windows -- stuff?
26991             return; 
26992         }
26993         if (node.nodeName == "#comment") {
26994             if (!this.allowComments) {
26995                 node.parentNode.removeChild(node);
26996             }
26997             // clean up silly Windows -- stuff?
26998             return; 
26999         }
27000         var lcname = node.tagName.toLowerCase();
27001         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
27002         // whitelist of tags..
27003         
27004         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
27005             // remove node.
27006             node.parentNode.removeChild(node);
27007             return;
27008             
27009         }
27010         
27011         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27012         
27013         // spans with no attributes - just remove them..
27014         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
27015             remove_keep_children = true;
27016         }
27017         
27018         // remove <a name=....> as rendering on yahoo mailer is borked with this.
27019         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27020         
27021         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27022         //    remove_keep_children = true;
27023         //}
27024         
27025         if (remove_keep_children) {
27026             this.cleanUpChildren(node);
27027             // inserts everything just before this node...
27028             while (node.childNodes.length) {
27029                 var cn = node.childNodes[0];
27030                 node.removeChild(cn);
27031                 node.parentNode.insertBefore(cn, node);
27032             }
27033             node.parentNode.removeChild(node);
27034             return;
27035         }
27036         
27037         if (!node.attributes || !node.attributes.length) {
27038             
27039           
27040             
27041             
27042             this.cleanUpChildren(node);
27043             return;
27044         }
27045         
27046         function cleanAttr(n,v)
27047         {
27048             
27049             if (v.match(/^\./) || v.match(/^\//)) {
27050                 return;
27051             }
27052             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27053                 return;
27054             }
27055             if (v.match(/^#/)) {
27056                 return;
27057             }
27058             if (v.match(/^\{/)) { // allow template editing.
27059                 return;
27060             }
27061 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27062             node.removeAttribute(n);
27063             
27064         }
27065         
27066         var cwhite = this.cwhite;
27067         var cblack = this.cblack;
27068             
27069         function cleanStyle(n,v)
27070         {
27071             if (v.match(/expression/)) { //XSS?? should we even bother..
27072                 node.removeAttribute(n);
27073                 return;
27074             }
27075             
27076             var parts = v.split(/;/);
27077             var clean = [];
27078             
27079             Roo.each(parts, function(p) {
27080                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27081                 if (!p.length) {
27082                     return true;
27083                 }
27084                 var l = p.split(':').shift().replace(/\s+/g,'');
27085                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27086                 
27087                 if ( cwhite.length && cblack.indexOf(l) > -1) {
27088 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27089                     //node.removeAttribute(n);
27090                     return true;
27091                 }
27092                 //Roo.log()
27093                 // only allow 'c whitelisted system attributes'
27094                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
27095 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27096                     //node.removeAttribute(n);
27097                     return true;
27098                 }
27099                 
27100                 
27101                  
27102                 
27103                 clean.push(p);
27104                 return true;
27105             });
27106             if (clean.length) { 
27107                 node.setAttribute(n, clean.join(';'));
27108             } else {
27109                 node.removeAttribute(n);
27110             }
27111             
27112         }
27113         
27114         
27115         for (var i = node.attributes.length-1; i > -1 ; i--) {
27116             var a = node.attributes[i];
27117             //console.log(a);
27118             
27119             if (a.name.toLowerCase().substr(0,2)=='on')  {
27120                 node.removeAttribute(a.name);
27121                 continue;
27122             }
27123             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27124                 node.removeAttribute(a.name);
27125                 continue;
27126             }
27127             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27128                 cleanAttr(a.name,a.value); // fixme..
27129                 continue;
27130             }
27131             if (a.name == 'style') {
27132                 cleanStyle(a.name,a.value);
27133                 continue;
27134             }
27135             /// clean up MS crap..
27136             // tecnically this should be a list of valid class'es..
27137             
27138             
27139             if (a.name == 'class') {
27140                 if (a.value.match(/^Mso/)) {
27141                     node.removeAttribute('class');
27142                 }
27143                 
27144                 if (a.value.match(/^body$/)) {
27145                     node.removeAttribute('class');
27146                 }
27147                 continue;
27148             }
27149             
27150             // style cleanup!?
27151             // class cleanup?
27152             
27153         }
27154         
27155         
27156         this.cleanUpChildren(node);
27157         
27158         
27159     },
27160     
27161     /**
27162      * Clean up MS wordisms...
27163      */
27164     cleanWord : function(node)
27165     {
27166         if (!node) {
27167             this.cleanWord(this.doc.body);
27168             return;
27169         }
27170         
27171         if(
27172                 node.nodeName == 'SPAN' &&
27173                 !node.hasAttributes() &&
27174                 node.childNodes.length == 1 &&
27175                 node.firstChild.nodeName == "#text"  
27176         ) {
27177             var textNode = node.firstChild;
27178             node.removeChild(textNode);
27179             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27180                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27181             }
27182             node.parentNode.insertBefore(textNode, node);
27183             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27184                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27185             }
27186             node.parentNode.removeChild(node);
27187         }
27188         
27189         if (node.nodeName == "#text") {
27190             // clean up silly Windows -- stuff?
27191             return; 
27192         }
27193         if (node.nodeName == "#comment") {
27194             node.parentNode.removeChild(node);
27195             // clean up silly Windows -- stuff?
27196             return; 
27197         }
27198         
27199         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27200             node.parentNode.removeChild(node);
27201             return;
27202         }
27203         //Roo.log(node.tagName);
27204         // remove - but keep children..
27205         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27206             //Roo.log('-- removed');
27207             while (node.childNodes.length) {
27208                 var cn = node.childNodes[0];
27209                 node.removeChild(cn);
27210                 node.parentNode.insertBefore(cn, node);
27211                 // move node to parent - and clean it..
27212                 this.cleanWord(cn);
27213             }
27214             node.parentNode.removeChild(node);
27215             /// no need to iterate chidlren = it's got none..
27216             //this.iterateChildren(node, this.cleanWord);
27217             return;
27218         }
27219         // clean styles
27220         if (node.className.length) {
27221             
27222             var cn = node.className.split(/\W+/);
27223             var cna = [];
27224             Roo.each(cn, function(cls) {
27225                 if (cls.match(/Mso[a-zA-Z]+/)) {
27226                     return;
27227                 }
27228                 cna.push(cls);
27229             });
27230             node.className = cna.length ? cna.join(' ') : '';
27231             if (!cna.length) {
27232                 node.removeAttribute("class");
27233             }
27234         }
27235         
27236         if (node.hasAttribute("lang")) {
27237             node.removeAttribute("lang");
27238         }
27239         
27240         if (node.hasAttribute("style")) {
27241             
27242             var styles = node.getAttribute("style").split(";");
27243             var nstyle = [];
27244             Roo.each(styles, function(s) {
27245                 if (!s.match(/:/)) {
27246                     return;
27247                 }
27248                 var kv = s.split(":");
27249                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27250                     return;
27251                 }
27252                 // what ever is left... we allow.
27253                 nstyle.push(s);
27254             });
27255             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27256             if (!nstyle.length) {
27257                 node.removeAttribute('style');
27258             }
27259         }
27260         this.iterateChildren(node, this.cleanWord);
27261         
27262         
27263         
27264     },
27265     /**
27266      * iterateChildren of a Node, calling fn each time, using this as the scole..
27267      * @param {DomNode} node node to iterate children of.
27268      * @param {Function} fn method of this class to call on each item.
27269      */
27270     iterateChildren : function(node, fn)
27271     {
27272         if (!node.childNodes.length) {
27273                 return;
27274         }
27275         for (var i = node.childNodes.length-1; i > -1 ; i--) {
27276            fn.call(this, node.childNodes[i])
27277         }
27278     },
27279     
27280     
27281     /**
27282      * cleanTableWidths.
27283      *
27284      * Quite often pasting from word etc.. results in tables with column and widths.
27285      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27286      *
27287      */
27288     cleanTableWidths : function(node)
27289     {
27290          
27291          
27292         if (!node) {
27293             this.cleanTableWidths(this.doc.body);
27294             return;
27295         }
27296         
27297         // ignore list...
27298         if (node.nodeName == "#text" || node.nodeName == "#comment") {
27299             return; 
27300         }
27301         Roo.log(node.tagName);
27302         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27303             this.iterateChildren(node, this.cleanTableWidths);
27304             return;
27305         }
27306         if (node.hasAttribute('width')) {
27307             node.removeAttribute('width');
27308         }
27309         
27310          
27311         if (node.hasAttribute("style")) {
27312             // pretty basic...
27313             
27314             var styles = node.getAttribute("style").split(";");
27315             var nstyle = [];
27316             Roo.each(styles, function(s) {
27317                 if (!s.match(/:/)) {
27318                     return;
27319                 }
27320                 var kv = s.split(":");
27321                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27322                     return;
27323                 }
27324                 // what ever is left... we allow.
27325                 nstyle.push(s);
27326             });
27327             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27328             if (!nstyle.length) {
27329                 node.removeAttribute('style');
27330             }
27331         }
27332         
27333         this.iterateChildren(node, this.cleanTableWidths);
27334         
27335         
27336     },
27337     
27338     
27339     
27340     
27341     domToHTML : function(currentElement, depth, nopadtext) {
27342         
27343         depth = depth || 0;
27344         nopadtext = nopadtext || false;
27345     
27346         if (!currentElement) {
27347             return this.domToHTML(this.doc.body);
27348         }
27349         
27350         //Roo.log(currentElement);
27351         var j;
27352         var allText = false;
27353         var nodeName = currentElement.nodeName;
27354         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27355         
27356         if  (nodeName == '#text') {
27357             
27358             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27359         }
27360         
27361         
27362         var ret = '';
27363         if (nodeName != 'BODY') {
27364              
27365             var i = 0;
27366             // Prints the node tagName, such as <A>, <IMG>, etc
27367             if (tagName) {
27368                 var attr = [];
27369                 for(i = 0; i < currentElement.attributes.length;i++) {
27370                     // quoting?
27371                     var aname = currentElement.attributes.item(i).name;
27372                     if (!currentElement.attributes.item(i).value.length) {
27373                         continue;
27374                     }
27375                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27376                 }
27377                 
27378                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27379             } 
27380             else {
27381                 
27382                 // eack
27383             }
27384         } else {
27385             tagName = false;
27386         }
27387         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27388             return ret;
27389         }
27390         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27391             nopadtext = true;
27392         }
27393         
27394         
27395         // Traverse the tree
27396         i = 0;
27397         var currentElementChild = currentElement.childNodes.item(i);
27398         var allText = true;
27399         var innerHTML  = '';
27400         lastnode = '';
27401         while (currentElementChild) {
27402             // Formatting code (indent the tree so it looks nice on the screen)
27403             var nopad = nopadtext;
27404             if (lastnode == 'SPAN') {
27405                 nopad  = true;
27406             }
27407             // text
27408             if  (currentElementChild.nodeName == '#text') {
27409                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27410                 toadd = nopadtext ? toadd : toadd.trim();
27411                 if (!nopad && toadd.length > 80) {
27412                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
27413                 }
27414                 innerHTML  += toadd;
27415                 
27416                 i++;
27417                 currentElementChild = currentElement.childNodes.item(i);
27418                 lastNode = '';
27419                 continue;
27420             }
27421             allText = false;
27422             
27423             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
27424                 
27425             // Recursively traverse the tree structure of the child node
27426             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
27427             lastnode = currentElementChild.nodeName;
27428             i++;
27429             currentElementChild=currentElement.childNodes.item(i);
27430         }
27431         
27432         ret += innerHTML;
27433         
27434         if (!allText) {
27435                 // The remaining code is mostly for formatting the tree
27436             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27437         }
27438         
27439         
27440         if (tagName) {
27441             ret+= "</"+tagName+">";
27442         }
27443         return ret;
27444         
27445     },
27446         
27447     applyBlacklists : function()
27448     {
27449         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27450         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27451         
27452         this.white = [];
27453         this.black = [];
27454         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27455             if (b.indexOf(tag) > -1) {
27456                 return;
27457             }
27458             this.white.push(tag);
27459             
27460         }, this);
27461         
27462         Roo.each(w, function(tag) {
27463             if (b.indexOf(tag) > -1) {
27464                 return;
27465             }
27466             if (this.white.indexOf(tag) > -1) {
27467                 return;
27468             }
27469             this.white.push(tag);
27470             
27471         }, this);
27472         
27473         
27474         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27475             if (w.indexOf(tag) > -1) {
27476                 return;
27477             }
27478             this.black.push(tag);
27479             
27480         }, this);
27481         
27482         Roo.each(b, function(tag) {
27483             if (w.indexOf(tag) > -1) {
27484                 return;
27485             }
27486             if (this.black.indexOf(tag) > -1) {
27487                 return;
27488             }
27489             this.black.push(tag);
27490             
27491         }, this);
27492         
27493         
27494         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27495         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27496         
27497         this.cwhite = [];
27498         this.cblack = [];
27499         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27500             if (b.indexOf(tag) > -1) {
27501                 return;
27502             }
27503             this.cwhite.push(tag);
27504             
27505         }, this);
27506         
27507         Roo.each(w, function(tag) {
27508             if (b.indexOf(tag) > -1) {
27509                 return;
27510             }
27511             if (this.cwhite.indexOf(tag) > -1) {
27512                 return;
27513             }
27514             this.cwhite.push(tag);
27515             
27516         }, this);
27517         
27518         
27519         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27520             if (w.indexOf(tag) > -1) {
27521                 return;
27522             }
27523             this.cblack.push(tag);
27524             
27525         }, this);
27526         
27527         Roo.each(b, function(tag) {
27528             if (w.indexOf(tag) > -1) {
27529                 return;
27530             }
27531             if (this.cblack.indexOf(tag) > -1) {
27532                 return;
27533             }
27534             this.cblack.push(tag);
27535             
27536         }, this);
27537     },
27538     
27539     setStylesheets : function(stylesheets)
27540     {
27541         if(typeof(stylesheets) == 'string'){
27542             Roo.get(this.iframe.contentDocument.head).createChild({
27543                 tag : 'link',
27544                 rel : 'stylesheet',
27545                 type : 'text/css',
27546                 href : stylesheets
27547             });
27548             
27549             return;
27550         }
27551         var _this = this;
27552      
27553         Roo.each(stylesheets, function(s) {
27554             if(!s.length){
27555                 return;
27556             }
27557             
27558             Roo.get(_this.iframe.contentDocument.head).createChild({
27559                 tag : 'link',
27560                 rel : 'stylesheet',
27561                 type : 'text/css',
27562                 href : s
27563             });
27564         });
27565
27566         
27567     },
27568     
27569     removeStylesheets : function()
27570     {
27571         var _this = this;
27572         
27573         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27574             s.remove();
27575         });
27576     },
27577     
27578     setStyle : function(style)
27579     {
27580         Roo.get(this.iframe.contentDocument.head).createChild({
27581             tag : 'style',
27582             type : 'text/css',
27583             html : style
27584         });
27585
27586         return;
27587     }
27588     
27589     // hide stuff that is not compatible
27590     /**
27591      * @event blur
27592      * @hide
27593      */
27594     /**
27595      * @event change
27596      * @hide
27597      */
27598     /**
27599      * @event focus
27600      * @hide
27601      */
27602     /**
27603      * @event specialkey
27604      * @hide
27605      */
27606     /**
27607      * @cfg {String} fieldClass @hide
27608      */
27609     /**
27610      * @cfg {String} focusClass @hide
27611      */
27612     /**
27613      * @cfg {String} autoCreate @hide
27614      */
27615     /**
27616      * @cfg {String} inputType @hide
27617      */
27618     /**
27619      * @cfg {String} invalidClass @hide
27620      */
27621     /**
27622      * @cfg {String} invalidText @hide
27623      */
27624     /**
27625      * @cfg {String} msgFx @hide
27626      */
27627     /**
27628      * @cfg {String} validateOnBlur @hide
27629      */
27630 });
27631
27632 Roo.HtmlEditorCore.white = [
27633         'area', 'br', 'img', 'input', 'hr', 'wbr',
27634         
27635        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27636        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27637        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27638        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27639        'table',   'ul',         'xmp', 
27640        
27641        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27642       'thead',   'tr', 
27643      
27644       'dir', 'menu', 'ol', 'ul', 'dl',
27645        
27646       'embed',  'object'
27647 ];
27648
27649
27650 Roo.HtmlEditorCore.black = [
27651     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27652         'applet', // 
27653         'base',   'basefont', 'bgsound', 'blink',  'body', 
27654         'frame',  'frameset', 'head',    'html',   'ilayer', 
27655         'iframe', 'layer',  'link',     'meta',    'object',   
27656         'script', 'style' ,'title',  'xml' // clean later..
27657 ];
27658 Roo.HtmlEditorCore.clean = [
27659     'script', 'style', 'title', 'xml'
27660 ];
27661 Roo.HtmlEditorCore.remove = [
27662     'font'
27663 ];
27664 // attributes..
27665
27666 Roo.HtmlEditorCore.ablack = [
27667     'on'
27668 ];
27669     
27670 Roo.HtmlEditorCore.aclean = [ 
27671     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27672 ];
27673
27674 // protocols..
27675 Roo.HtmlEditorCore.pwhite= [
27676         'http',  'https',  'mailto'
27677 ];
27678
27679 // white listed style attributes.
27680 Roo.HtmlEditorCore.cwhite= [
27681       //  'text-align', /// default is to allow most things..
27682       
27683          
27684 //        'font-size'//??
27685 ];
27686
27687 // black listed style attributes.
27688 Roo.HtmlEditorCore.cblack= [
27689       //  'font-size' -- this can be set by the project 
27690 ];
27691
27692
27693 Roo.HtmlEditorCore.swapCodes   =[ 
27694     [    8211, "&#8211;" ], 
27695     [    8212, "&#8212;" ], 
27696     [    8216,  "'" ],  
27697     [    8217, "'" ],  
27698     [    8220, '"' ],  
27699     [    8221, '"' ],  
27700     [    8226, "*" ],  
27701     [    8230, "..." ]
27702 ]; 
27703
27704     /*
27705  * - LGPL
27706  *
27707  * HtmlEditor
27708  * 
27709  */
27710
27711 /**
27712  * @class Roo.bootstrap.form.HtmlEditor
27713  * @extends Roo.bootstrap.form.TextArea
27714  * Bootstrap HtmlEditor class
27715
27716  * @constructor
27717  * Create a new HtmlEditor
27718  * @param {Object} config The config object
27719  */
27720
27721 Roo.bootstrap.form.HtmlEditor = function(config){
27722     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27723     if (!this.toolbars) {
27724         this.toolbars = [];
27725     }
27726     
27727     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27728     this.addEvents({
27729             /**
27730              * @event initialize
27731              * Fires when the editor is fully initialized (including the iframe)
27732              * @param {HtmlEditor} this
27733              */
27734             initialize: true,
27735             /**
27736              * @event activate
27737              * Fires when the editor is first receives the focus. Any insertion must wait
27738              * until after this event.
27739              * @param {HtmlEditor} this
27740              */
27741             activate: true,
27742              /**
27743              * @event beforesync
27744              * Fires before the textarea is updated with content from the editor iframe. Return false
27745              * to cancel the sync.
27746              * @param {HtmlEditor} this
27747              * @param {String} html
27748              */
27749             beforesync: true,
27750              /**
27751              * @event beforepush
27752              * Fires before the iframe editor is updated with content from the textarea. Return false
27753              * to cancel the push.
27754              * @param {HtmlEditor} this
27755              * @param {String} html
27756              */
27757             beforepush: true,
27758              /**
27759              * @event sync
27760              * Fires when the textarea is updated with content from the editor iframe.
27761              * @param {HtmlEditor} this
27762              * @param {String} html
27763              */
27764             sync: true,
27765              /**
27766              * @event push
27767              * Fires when the iframe editor is updated with content from the textarea.
27768              * @param {HtmlEditor} this
27769              * @param {String} html
27770              */
27771             push: true,
27772              /**
27773              * @event editmodechange
27774              * Fires when the editor switches edit modes
27775              * @param {HtmlEditor} this
27776              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27777              */
27778             editmodechange: true,
27779             /**
27780              * @event editorevent
27781              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27782              * @param {HtmlEditor} this
27783              */
27784             editorevent: true,
27785             /**
27786              * @event firstfocus
27787              * Fires when on first focus - needed by toolbars..
27788              * @param {HtmlEditor} this
27789              */
27790             firstfocus: true,
27791             /**
27792              * @event autosave
27793              * Auto save the htmlEditor value as a file into Events
27794              * @param {HtmlEditor} this
27795              */
27796             autosave: true,
27797             /**
27798              * @event savedpreview
27799              * preview the saved version of htmlEditor
27800              * @param {HtmlEditor} this
27801              */
27802             savedpreview: true
27803         });
27804 };
27805
27806
27807 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
27808     
27809     
27810       /**
27811      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27812      */
27813     toolbars : false,
27814     
27815      /**
27816     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27817     */
27818     btns : [],
27819    
27820      /**
27821      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27822      *                        Roo.resizable.
27823      */
27824     resizable : false,
27825      /**
27826      * @cfg {Number} height (in pixels)
27827      */   
27828     height: 300,
27829    /**
27830      * @cfg {Number} width (in pixels)
27831      */   
27832     width: false,
27833     
27834     /**
27835      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27836      * 
27837      */
27838     stylesheets: false,
27839     
27840     // id of frame..
27841     frameId: false,
27842     
27843     // private properties
27844     validationEvent : false,
27845     deferHeight: true,
27846     initialized : false,
27847     activated : false,
27848     
27849     onFocus : Roo.emptyFn,
27850     iframePad:3,
27851     hideMode:'offsets',
27852     
27853     tbContainer : false,
27854     
27855     bodyCls : '',
27856     
27857     toolbarContainer :function() {
27858         return this.wrap.select('.x-html-editor-tb',true).first();
27859     },
27860
27861     /**
27862      * Protected method that will not generally be called directly. It
27863      * is called when the editor creates its toolbar. Override this method if you need to
27864      * add custom toolbar buttons.
27865      * @param {HtmlEditor} editor
27866      */
27867     createToolbar : function(){
27868         Roo.log('renewing');
27869         Roo.log("create toolbars");
27870         
27871         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27872         this.toolbars[0].render(this.toolbarContainer());
27873         
27874         return;
27875         
27876 //        if (!editor.toolbars || !editor.toolbars.length) {
27877 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27878 //        }
27879 //        
27880 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27881 //            editor.toolbars[i] = Roo.factory(
27882 //                    typeof(editor.toolbars[i]) == 'string' ?
27883 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27884 //                Roo.bootstrap.form.HtmlEditor);
27885 //            editor.toolbars[i].init(editor);
27886 //        }
27887     },
27888
27889      
27890     // private
27891     onRender : function(ct, position)
27892     {
27893        // Roo.log("Call onRender: " + this.xtype);
27894         var _t = this;
27895         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27896       
27897         this.wrap = this.inputEl().wrap({
27898             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27899         });
27900         
27901         this.editorcore.onRender(ct, position);
27902          
27903         if (this.resizable) {
27904             this.resizeEl = new Roo.Resizable(this.wrap, {
27905                 pinned : true,
27906                 wrap: true,
27907                 dynamic : true,
27908                 minHeight : this.height,
27909                 height: this.height,
27910                 handles : this.resizable,
27911                 width: this.width,
27912                 listeners : {
27913                     resize : function(r, w, h) {
27914                         _t.onResize(w,h); // -something
27915                     }
27916                 }
27917             });
27918             
27919         }
27920         this.createToolbar(this);
27921        
27922         
27923         if(!this.width && this.resizable){
27924             this.setSize(this.wrap.getSize());
27925         }
27926         if (this.resizeEl) {
27927             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27928             // should trigger onReize..
27929         }
27930         
27931     },
27932
27933     // private
27934     onResize : function(w, h)
27935     {
27936         Roo.log('resize: ' +w + ',' + h );
27937         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27938         var ew = false;
27939         var eh = false;
27940         
27941         if(this.inputEl() ){
27942             if(typeof w == 'number'){
27943                 var aw = w - this.wrap.getFrameWidth('lr');
27944                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27945                 ew = aw;
27946             }
27947             if(typeof h == 'number'){
27948                  var tbh = -11;  // fixme it needs to tool bar size!
27949                 for (var i =0; i < this.toolbars.length;i++) {
27950                     // fixme - ask toolbars for heights?
27951                     tbh += this.toolbars[i].el.getHeight();
27952                     //if (this.toolbars[i].footer) {
27953                     //    tbh += this.toolbars[i].footer.el.getHeight();
27954                     //}
27955                 }
27956               
27957                 
27958                 
27959                 
27960                 
27961                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27962                 ah -= 5; // knock a few pixes off for look..
27963                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27964                 var eh = ah;
27965             }
27966         }
27967         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27968         this.editorcore.onResize(ew,eh);
27969         
27970     },
27971
27972     /**
27973      * Toggles the editor between standard and source edit mode.
27974      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27975      */
27976     toggleSourceEdit : function(sourceEditMode)
27977     {
27978         this.editorcore.toggleSourceEdit(sourceEditMode);
27979         
27980         if(this.editorcore.sourceEditMode){
27981             Roo.log('editor - showing textarea');
27982             
27983 //            Roo.log('in');
27984 //            Roo.log(this.syncValue());
27985             this.syncValue();
27986             this.inputEl().removeClass(['hide', 'x-hidden']);
27987             this.inputEl().dom.removeAttribute('tabIndex');
27988             this.inputEl().focus();
27989         }else{
27990             Roo.log('editor - hiding textarea');
27991 //            Roo.log('out')
27992 //            Roo.log(this.pushValue()); 
27993             this.pushValue();
27994             
27995             this.inputEl().addClass(['hide', 'x-hidden']);
27996             this.inputEl().dom.setAttribute('tabIndex', -1);
27997             //this.deferFocus();
27998         }
27999          
28000         if(this.resizable){
28001             this.setSize(this.wrap.getSize());
28002         }
28003         
28004         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
28005     },
28006  
28007     // private (for BoxComponent)
28008     adjustSize : Roo.BoxComponent.prototype.adjustSize,
28009
28010     // private (for BoxComponent)
28011     getResizeEl : function(){
28012         return this.wrap;
28013     },
28014
28015     // private (for BoxComponent)
28016     getPositionEl : function(){
28017         return this.wrap;
28018     },
28019
28020     // private
28021     initEvents : function(){
28022         this.originalValue = this.getValue();
28023     },
28024
28025 //    /**
28026 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28027 //     * @method
28028 //     */
28029 //    markInvalid : Roo.emptyFn,
28030 //    /**
28031 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28032 //     * @method
28033 //     */
28034 //    clearInvalid : Roo.emptyFn,
28035
28036     setValue : function(v){
28037         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28038         this.editorcore.pushValue();
28039     },
28040
28041      
28042     // private
28043     deferFocus : function(){
28044         this.focus.defer(10, this);
28045     },
28046
28047     // doc'ed in Field
28048     focus : function(){
28049         this.editorcore.focus();
28050         
28051     },
28052       
28053
28054     // private
28055     onDestroy : function(){
28056         
28057         
28058         
28059         if(this.rendered){
28060             
28061             for (var i =0; i < this.toolbars.length;i++) {
28062                 // fixme - ask toolbars for heights?
28063                 this.toolbars[i].onDestroy();
28064             }
28065             
28066             this.wrap.dom.innerHTML = '';
28067             this.wrap.remove();
28068         }
28069     },
28070
28071     // private
28072     onFirstFocus : function(){
28073         //Roo.log("onFirstFocus");
28074         this.editorcore.onFirstFocus();
28075          for (var i =0; i < this.toolbars.length;i++) {
28076             this.toolbars[i].onFirstFocus();
28077         }
28078         
28079     },
28080     
28081     // private
28082     syncValue : function()
28083     {   
28084         this.editorcore.syncValue();
28085     },
28086     
28087     pushValue : function()
28088     {   
28089         this.editorcore.pushValue();
28090     }
28091      
28092     
28093     // hide stuff that is not compatible
28094     /**
28095      * @event blur
28096      * @hide
28097      */
28098     /**
28099      * @event change
28100      * @hide
28101      */
28102     /**
28103      * @event focus
28104      * @hide
28105      */
28106     /**
28107      * @event specialkey
28108      * @hide
28109      */
28110     /**
28111      * @cfg {String} fieldClass @hide
28112      */
28113     /**
28114      * @cfg {String} focusClass @hide
28115      */
28116     /**
28117      * @cfg {String} autoCreate @hide
28118      */
28119     /**
28120      * @cfg {String} inputType @hide
28121      */
28122      
28123     /**
28124      * @cfg {String} invalidText @hide
28125      */
28126     /**
28127      * @cfg {String} msgFx @hide
28128      */
28129     /**
28130      * @cfg {String} validateOnBlur @hide
28131      */
28132 });
28133  
28134     
28135    
28136    
28137    
28138       
28139 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28140 /**
28141  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28142  * @parent Roo.bootstrap.form.HtmlEditor
28143  * @extends Roo.bootstrap.nav.Simplebar
28144  * Basic Toolbar
28145  * 
28146  * @example
28147  * Usage:
28148  *
28149  new Roo.bootstrap.form.HtmlEditor({
28150     ....
28151     toolbars : [
28152         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28153             disable : { fonts: 1 , format: 1, ..., ... , ...],
28154             btns : [ .... ]
28155         })
28156     }
28157      
28158  * 
28159  * @cfg {Object} disable List of elements to disable..
28160  * @cfg {Array} btns List of additional buttons.
28161  * 
28162  * 
28163  * NEEDS Extra CSS? 
28164  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28165  */
28166  
28167 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28168 {
28169     
28170     Roo.apply(this, config);
28171     
28172     // default disabled, based on 'good practice'..
28173     this.disable = this.disable || {};
28174     Roo.applyIf(this.disable, {
28175         fontSize : true,
28176         colors : true,
28177         specialElements : true
28178     });
28179     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28180     
28181     this.editor = config.editor;
28182     this.editorcore = config.editor.editorcore;
28183     
28184     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28185     
28186     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28187     // dont call parent... till later.
28188 }
28189 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
28190      
28191     bar : true,
28192     
28193     editor : false,
28194     editorcore : false,
28195     
28196     
28197     formats : [
28198         "p" ,  
28199         "h1","h2","h3","h4","h5","h6", 
28200         "pre", "code", 
28201         "abbr", "acronym", "address", "cite", "samp", "var",
28202         'div','span'
28203     ],
28204     
28205     onRender : function(ct, position)
28206     {
28207        // Roo.log("Call onRender: " + this.xtype);
28208         
28209        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28210        Roo.log(this.el);
28211        this.el.dom.style.marginBottom = '0';
28212        var _this = this;
28213        var editorcore = this.editorcore;
28214        var editor= this.editor;
28215        
28216        var children = [];
28217        var btn = function(id,cmd , toggle, handler, html){
28218        
28219             var  event = toggle ? 'toggle' : 'click';
28220        
28221             var a = {
28222                 size : 'sm',
28223                 xtype: 'Button',
28224                 xns: Roo.bootstrap,
28225                 //glyphicon : id,
28226                 fa: id,
28227                 cmd : id || cmd,
28228                 enableToggle:toggle !== false,
28229                 html : html || '',
28230                 pressed : toggle ? false : null,
28231                 listeners : {}
28232             };
28233             a.listeners[toggle ? 'toggle' : 'click'] = function() {
28234                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
28235             };
28236             children.push(a);
28237             return a;
28238        }
28239        
28240     //    var cb_box = function...
28241         
28242         var style = {
28243                 xtype: 'Button',
28244                 size : 'sm',
28245                 xns: Roo.bootstrap,
28246                 fa : 'font',
28247                 //html : 'submit'
28248                 menu : {
28249                     xtype: 'Menu',
28250                     xns: Roo.bootstrap,
28251                     items:  []
28252                 }
28253         };
28254         Roo.each(this.formats, function(f) {
28255             style.menu.items.push({
28256                 xtype :'MenuItem',
28257                 xns: Roo.bootstrap,
28258                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28259                 tagname : f,
28260                 listeners : {
28261                     click : function()
28262                     {
28263                         editorcore.insertTag(this.tagname);
28264                         editor.focus();
28265                     }
28266                 }
28267                 
28268             });
28269         });
28270         children.push(style);   
28271         
28272         btn('bold',false,true);
28273         btn('italic',false,true);
28274         btn('align-left', 'justifyleft',true);
28275         btn('align-center', 'justifycenter',true);
28276         btn('align-right' , 'justifyright',true);
28277         btn('link', false, false, function(btn) {
28278             //Roo.log("create link?");
28279             var url = prompt(this.createLinkText, this.defaultLinkValue);
28280             if(url && url != 'http:/'+'/'){
28281                 this.editorcore.relayCmd('createlink', url);
28282             }
28283         }),
28284         btn('list','insertunorderedlist',true);
28285         btn('pencil', false,true, function(btn){
28286                 Roo.log(this);
28287                 this.toggleSourceEdit(btn.pressed);
28288         });
28289         
28290         if (this.editor.btns.length > 0) {
28291             for (var i = 0; i<this.editor.btns.length; i++) {
28292                 children.push(this.editor.btns[i]);
28293             }
28294         }
28295         
28296         /*
28297         var cog = {
28298                 xtype: 'Button',
28299                 size : 'sm',
28300                 xns: Roo.bootstrap,
28301                 glyphicon : 'cog',
28302                 //html : 'submit'
28303                 menu : {
28304                     xtype: 'Menu',
28305                     xns: Roo.bootstrap,
28306                     items:  []
28307                 }
28308         };
28309         
28310         cog.menu.items.push({
28311             xtype :'MenuItem',
28312             xns: Roo.bootstrap,
28313             html : Clean styles,
28314             tagname : f,
28315             listeners : {
28316                 click : function()
28317                 {
28318                     editorcore.insertTag(this.tagname);
28319                     editor.focus();
28320                 }
28321             }
28322             
28323         });
28324        */
28325         
28326          
28327        this.xtype = 'NavSimplebar';
28328         
28329         for(var i=0;i< children.length;i++) {
28330             
28331             this.buttons.add(this.addxtypeChild(children[i]));
28332             
28333         }
28334         
28335         editor.on('editorevent', this.updateToolbar, this);
28336     },
28337     onBtnClick : function(id)
28338     {
28339        this.editorcore.relayCmd(id);
28340        this.editorcore.focus();
28341     },
28342     
28343     /**
28344      * Protected method that will not generally be called directly. It triggers
28345      * a toolbar update by reading the markup state of the current selection in the editor.
28346      */
28347     updateToolbar: function(){
28348
28349         if(!this.editorcore.activated){
28350             this.editor.onFirstFocus(); // is this neeed?
28351             return;
28352         }
28353
28354         var btns = this.buttons; 
28355         var doc = this.editorcore.doc;
28356         btns.get('bold').setActive(doc.queryCommandState('bold'));
28357         btns.get('italic').setActive(doc.queryCommandState('italic'));
28358         //btns.get('underline').setActive(doc.queryCommandState('underline'));
28359         
28360         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28361         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28362         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28363         
28364         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28365         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28366          /*
28367         
28368         var ans = this.editorcore.getAllAncestors();
28369         if (this.formatCombo) {
28370             
28371             
28372             var store = this.formatCombo.store;
28373             this.formatCombo.setValue("");
28374             for (var i =0; i < ans.length;i++) {
28375                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28376                     // select it..
28377                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28378                     break;
28379                 }
28380             }
28381         }
28382         
28383         
28384         
28385         // hides menus... - so this cant be on a menu...
28386         Roo.bootstrap.MenuMgr.hideAll();
28387         */
28388         Roo.bootstrap.menu.Manager.hideAll();
28389         //this.editorsyncValue();
28390     },
28391     onFirstFocus: function() {
28392         this.buttons.each(function(item){
28393            item.enable();
28394         });
28395     },
28396     toggleSourceEdit : function(sourceEditMode){
28397         
28398           
28399         if(sourceEditMode){
28400             Roo.log("disabling buttons");
28401            this.buttons.each( function(item){
28402                 if(item.cmd != 'pencil'){
28403                     item.disable();
28404                 }
28405             });
28406           
28407         }else{
28408             Roo.log("enabling buttons");
28409             if(this.editorcore.initialized){
28410                 this.buttons.each( function(item){
28411                     item.enable();
28412                 });
28413             }
28414             
28415         }
28416         Roo.log("calling toggole on editor");
28417         // tell the editor that it's been pressed..
28418         this.editor.toggleSourceEdit(sourceEditMode);
28419        
28420     }
28421 });
28422
28423
28424
28425
28426  
28427 /*
28428  * - LGPL
28429  */
28430
28431 /**
28432  * @class Roo.bootstrap.form.Markdown
28433  * @extends Roo.bootstrap.form.TextArea
28434  * Bootstrap Showdown editable area
28435  * @cfg {string} content
28436  * 
28437  * @constructor
28438  * Create a new Showdown
28439  */
28440
28441 Roo.bootstrap.form.Markdown = function(config){
28442     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28443    
28444 };
28445
28446 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
28447     
28448     editing :false,
28449     
28450     initEvents : function()
28451     {
28452         
28453         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28454         this.markdownEl = this.el.createChild({
28455             cls : 'roo-markdown-area'
28456         });
28457         this.inputEl().addClass('d-none');
28458         if (this.getValue() == '') {
28459             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28460             
28461         } else {
28462             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28463         }
28464         this.markdownEl.on('click', this.toggleTextEdit, this);
28465         this.on('blur', this.toggleTextEdit, this);
28466         this.on('specialkey', this.resizeTextArea, this);
28467     },
28468     
28469     toggleTextEdit : function()
28470     {
28471         var sh = this.markdownEl.getHeight();
28472         this.inputEl().addClass('d-none');
28473         this.markdownEl.addClass('d-none');
28474         if (!this.editing) {
28475             // show editor?
28476             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28477             this.inputEl().removeClass('d-none');
28478             this.inputEl().focus();
28479             this.editing = true;
28480             return;
28481         }
28482         // show showdown...
28483         this.updateMarkdown();
28484         this.markdownEl.removeClass('d-none');
28485         this.editing = false;
28486         return;
28487     },
28488     updateMarkdown : function()
28489     {
28490         if (this.getValue() == '') {
28491             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28492             return;
28493         }
28494  
28495         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28496     },
28497     
28498     resizeTextArea: function () {
28499         
28500         var sh = 100;
28501         Roo.log([sh, this.getValue().split("\n").length * 30]);
28502         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28503     },
28504     setValue : function(val)
28505     {
28506         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28507         if (!this.editing) {
28508             this.updateMarkdown();
28509         }
28510         
28511     },
28512     focus : function()
28513     {
28514         if (!this.editing) {
28515             this.toggleTextEdit();
28516         }
28517         
28518     }
28519
28520
28521 });/*
28522  * Based on:
28523  * Ext JS Library 1.1.1
28524  * Copyright(c) 2006-2007, Ext JS, LLC.
28525  *
28526  * Originally Released Under LGPL - original licence link has changed is not relivant.
28527  *
28528  * Fork - LGPL
28529  * <script type="text/javascript">
28530  */
28531  
28532 /**
28533  * @class Roo.bootstrap.PagingToolbar
28534  * @extends Roo.bootstrap.nav.Simplebar
28535  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28536  * @constructor
28537  * Create a new PagingToolbar
28538  * @param {Object} config The config object
28539  * @param {Roo.data.Store} store
28540  */
28541 Roo.bootstrap.PagingToolbar = function(config)
28542 {
28543     // old args format still supported... - xtype is prefered..
28544         // created from xtype...
28545     
28546     this.ds = config.dataSource;
28547     
28548     if (config.store && !this.ds) {
28549         this.store= Roo.factory(config.store, Roo.data);
28550         this.ds = this.store;
28551         this.ds.xmodule = this.xmodule || false;
28552     }
28553     
28554     this.toolbarItems = [];
28555     if (config.items) {
28556         this.toolbarItems = config.items;
28557     }
28558     
28559     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28560     
28561     this.cursor = 0;
28562     
28563     if (this.ds) { 
28564         this.bind(this.ds);
28565     }
28566     
28567     if (Roo.bootstrap.version == 4) {
28568         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28569     } else {
28570         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28571     }
28572     
28573 };
28574
28575 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28576     /**
28577      * @cfg {Roo.bootstrap.Button} buttons[]
28578      * Buttons for the toolbar
28579      */
28580      /**
28581      * @cfg {Roo.data.Store} store
28582      * The underlying data store providing the paged data
28583      */
28584     /**
28585      * @cfg {String/HTMLElement/Element} container
28586      * container The id or element that will contain the toolbar
28587      */
28588     /**
28589      * @cfg {Boolean} displayInfo
28590      * True to display the displayMsg (defaults to false)
28591      */
28592     /**
28593      * @cfg {Number} pageSize
28594      * The number of records to display per page (defaults to 20)
28595      */
28596     pageSize: 20,
28597     /**
28598      * @cfg {String} displayMsg
28599      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28600      */
28601     displayMsg : 'Displaying {0} - {1} of {2}',
28602     /**
28603      * @cfg {String} emptyMsg
28604      * The message to display when no records are found (defaults to "No data to display")
28605      */
28606     emptyMsg : 'No data to display',
28607     /**
28608      * Customizable piece of the default paging text (defaults to "Page")
28609      * @type String
28610      */
28611     beforePageText : "Page",
28612     /**
28613      * Customizable piece of the default paging text (defaults to "of %0")
28614      * @type String
28615      */
28616     afterPageText : "of {0}",
28617     /**
28618      * Customizable piece of the default paging text (defaults to "First Page")
28619      * @type String
28620      */
28621     firstText : "First Page",
28622     /**
28623      * Customizable piece of the default paging text (defaults to "Previous Page")
28624      * @type String
28625      */
28626     prevText : "Previous Page",
28627     /**
28628      * Customizable piece of the default paging text (defaults to "Next Page")
28629      * @type String
28630      */
28631     nextText : "Next Page",
28632     /**
28633      * Customizable piece of the default paging text (defaults to "Last Page")
28634      * @type String
28635      */
28636     lastText : "Last Page",
28637     /**
28638      * Customizable piece of the default paging text (defaults to "Refresh")
28639      * @type String
28640      */
28641     refreshText : "Refresh",
28642
28643     buttons : false,
28644     // private
28645     onRender : function(ct, position) 
28646     {
28647         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28648         this.navgroup.parentId = this.id;
28649         this.navgroup.onRender(this.el, null);
28650         // add the buttons to the navgroup
28651         
28652         if(this.displayInfo){
28653             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28654             this.displayEl = this.el.select('.x-paging-info', true).first();
28655 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28656 //            this.displayEl = navel.el.select('span',true).first();
28657         }
28658         
28659         var _this = this;
28660         
28661         if(this.buttons){
28662             Roo.each(_this.buttons, function(e){ // this might need to use render????
28663                Roo.factory(e).render(_this.el);
28664             });
28665         }
28666             
28667         Roo.each(_this.toolbarItems, function(e) {
28668             _this.navgroup.addItem(e);
28669         });
28670         
28671         
28672         this.first = this.navgroup.addItem({
28673             tooltip: this.firstText,
28674             cls: "prev btn-outline-secondary",
28675             html : ' <i class="fa fa-step-backward"></i>',
28676             disabled: true,
28677             preventDefault: true,
28678             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28679         });
28680         
28681         this.prev =  this.navgroup.addItem({
28682             tooltip: this.prevText,
28683             cls: "prev btn-outline-secondary",
28684             html : ' <i class="fa fa-backward"></i>',
28685             disabled: true,
28686             preventDefault: true,
28687             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28688         });
28689     //this.addSeparator();
28690         
28691         
28692         var field = this.navgroup.addItem( {
28693             tagtype : 'span',
28694             cls : 'x-paging-position  btn-outline-secondary',
28695              disabled: true,
28696             html : this.beforePageText  +
28697                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28698                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28699          } ); //?? escaped?
28700         
28701         this.field = field.el.select('input', true).first();
28702         this.field.on("keydown", this.onPagingKeydown, this);
28703         this.field.on("focus", function(){this.dom.select();});
28704     
28705     
28706         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28707         //this.field.setHeight(18);
28708         //this.addSeparator();
28709         this.next = this.navgroup.addItem({
28710             tooltip: this.nextText,
28711             cls: "next btn-outline-secondary",
28712             html : ' <i class="fa fa-forward"></i>',
28713             disabled: true,
28714             preventDefault: true,
28715             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28716         });
28717         this.last = this.navgroup.addItem({
28718             tooltip: this.lastText,
28719             html : ' <i class="fa fa-step-forward"></i>',
28720             cls: "next btn-outline-secondary",
28721             disabled: true,
28722             preventDefault: true,
28723             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28724         });
28725     //this.addSeparator();
28726         this.loading = this.navgroup.addItem({
28727             tooltip: this.refreshText,
28728             cls: "btn-outline-secondary",
28729             html : ' <i class="fa fa-refresh"></i>',
28730             preventDefault: true,
28731             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28732         });
28733         
28734     },
28735
28736     // private
28737     updateInfo : function(){
28738         if(this.displayEl){
28739             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28740             var msg = count == 0 ?
28741                 this.emptyMsg :
28742                 String.format(
28743                     this.displayMsg,
28744                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28745                 );
28746             this.displayEl.update(msg);
28747         }
28748     },
28749
28750     // private
28751     onLoad : function(ds, r, o)
28752     {
28753         this.cursor = o.params && o.params.start ? o.params.start : 0;
28754         
28755         var d = this.getPageData(),
28756             ap = d.activePage,
28757             ps = d.pages;
28758         
28759         
28760         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28761         this.field.dom.value = ap;
28762         this.first.setDisabled(ap == 1);
28763         this.prev.setDisabled(ap == 1);
28764         this.next.setDisabled(ap == ps);
28765         this.last.setDisabled(ap == ps);
28766         this.loading.enable();
28767         this.updateInfo();
28768     },
28769
28770     // private
28771     getPageData : function(){
28772         var total = this.ds.getTotalCount();
28773         return {
28774             total : total,
28775             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28776             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28777         };
28778     },
28779
28780     // private
28781     onLoadError : function(){
28782         this.loading.enable();
28783     },
28784
28785     // private
28786     onPagingKeydown : function(e){
28787         var k = e.getKey();
28788         var d = this.getPageData();
28789         if(k == e.RETURN){
28790             var v = this.field.dom.value, pageNum;
28791             if(!v || isNaN(pageNum = parseInt(v, 10))){
28792                 this.field.dom.value = d.activePage;
28793                 return;
28794             }
28795             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28796             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28797             e.stopEvent();
28798         }
28799         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))
28800         {
28801           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28802           this.field.dom.value = pageNum;
28803           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28804           e.stopEvent();
28805         }
28806         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28807         {
28808           var v = this.field.dom.value, pageNum; 
28809           var increment = (e.shiftKey) ? 10 : 1;
28810           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28811                 increment *= -1;
28812           }
28813           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28814             this.field.dom.value = d.activePage;
28815             return;
28816           }
28817           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28818           {
28819             this.field.dom.value = parseInt(v, 10) + increment;
28820             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28821             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28822           }
28823           e.stopEvent();
28824         }
28825     },
28826
28827     // private
28828     beforeLoad : function(){
28829         if(this.loading){
28830             this.loading.disable();
28831         }
28832     },
28833
28834     // private
28835     onClick : function(which){
28836         
28837         var ds = this.ds;
28838         if (!ds) {
28839             return;
28840         }
28841         
28842         switch(which){
28843             case "first":
28844                 ds.load({params:{start: 0, limit: this.pageSize}});
28845             break;
28846             case "prev":
28847                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28848             break;
28849             case "next":
28850                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28851             break;
28852             case "last":
28853                 var total = ds.getTotalCount();
28854                 var extra = total % this.pageSize;
28855                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28856                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28857             break;
28858             case "refresh":
28859                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28860             break;
28861         }
28862     },
28863
28864     /**
28865      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28866      * @param {Roo.data.Store} store The data store to unbind
28867      */
28868     unbind : function(ds){
28869         ds.un("beforeload", this.beforeLoad, this);
28870         ds.un("load", this.onLoad, this);
28871         ds.un("loadexception", this.onLoadError, this);
28872         ds.un("remove", this.updateInfo, this);
28873         ds.un("add", this.updateInfo, this);
28874         this.ds = undefined;
28875     },
28876
28877     /**
28878      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28879      * @param {Roo.data.Store} store The data store to bind
28880      */
28881     bind : function(ds){
28882         ds.on("beforeload", this.beforeLoad, this);
28883         ds.on("load", this.onLoad, this);
28884         ds.on("loadexception", this.onLoadError, this);
28885         ds.on("remove", this.updateInfo, this);
28886         ds.on("add", this.updateInfo, this);
28887         this.ds = ds;
28888     }
28889 });/*
28890  * - LGPL
28891  *
28892  * element
28893  * 
28894  */
28895
28896 /**
28897  * @class Roo.bootstrap.MessageBar
28898  * @extends Roo.bootstrap.Component
28899  * Bootstrap MessageBar class
28900  * @cfg {String} html contents of the MessageBar
28901  * @cfg {String} weight (info | success | warning | danger) default info
28902  * @cfg {String} beforeClass insert the bar before the given class
28903  * @cfg {Boolean} closable (true | false) default false
28904  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28905  * 
28906  * @constructor
28907  * Create a new Element
28908  * @param {Object} config The config object
28909  */
28910
28911 Roo.bootstrap.MessageBar = function(config){
28912     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28913 };
28914
28915 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28916     
28917     html: '',
28918     weight: 'info',
28919     closable: false,
28920     fixed: false,
28921     beforeClass: 'bootstrap-sticky-wrap',
28922     
28923     getAutoCreate : function(){
28924         
28925         var cfg = {
28926             tag: 'div',
28927             cls: 'alert alert-dismissable alert-' + this.weight,
28928             cn: [
28929                 {
28930                     tag: 'span',
28931                     cls: 'message',
28932                     html: this.html || ''
28933                 }
28934             ]
28935         };
28936         
28937         if(this.fixed){
28938             cfg.cls += ' alert-messages-fixed';
28939         }
28940         
28941         if(this.closable){
28942             cfg.cn.push({
28943                 tag: 'button',
28944                 cls: 'close',
28945                 html: 'x'
28946             });
28947         }
28948         
28949         return cfg;
28950     },
28951     
28952     onRender : function(ct, position)
28953     {
28954         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28955         
28956         if(!this.el){
28957             var cfg = Roo.apply({},  this.getAutoCreate());
28958             cfg.id = Roo.id();
28959             
28960             if (this.cls) {
28961                 cfg.cls += ' ' + this.cls;
28962             }
28963             if (this.style) {
28964                 cfg.style = this.style;
28965             }
28966             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28967             
28968             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28969         }
28970         
28971         this.el.select('>button.close').on('click', this.hide, this);
28972         
28973     },
28974     
28975     show : function()
28976     {
28977         if (!this.rendered) {
28978             this.render();
28979         }
28980         
28981         this.el.show();
28982         
28983         this.fireEvent('show', this);
28984         
28985     },
28986     
28987     hide : function()
28988     {
28989         if (!this.rendered) {
28990             this.render();
28991         }
28992         
28993         this.el.hide();
28994         
28995         this.fireEvent('hide', this);
28996     },
28997     
28998     update : function()
28999     {
29000 //        var e = this.el.dom.firstChild;
29001 //        
29002 //        if(this.closable){
29003 //            e = e.nextSibling;
29004 //        }
29005 //        
29006 //        e.data = this.html || '';
29007
29008         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29009     }
29010    
29011 });
29012
29013  
29014
29015      /*
29016  * - LGPL
29017  *
29018  * Graph
29019  * 
29020  */
29021
29022
29023 /**
29024  * @class Roo.bootstrap.Graph
29025  * @extends Roo.bootstrap.Component
29026  * Bootstrap Graph class
29027 > Prameters
29028  -sm {number} sm 4
29029  -md {number} md 5
29030  @cfg {String} graphtype  bar | vbar | pie
29031  @cfg {number} g_x coodinator | centre x (pie)
29032  @cfg {number} g_y coodinator | centre y (pie)
29033  @cfg {number} g_r radius (pie)
29034  @cfg {number} g_height height of the chart (respected by all elements in the set)
29035  @cfg {number} g_width width of the chart (respected by all elements in the set)
29036  @cfg {Object} title The title of the chart
29037     
29038  -{Array}  values
29039  -opts (object) options for the chart 
29040      o {
29041      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29042      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29043      o vgutter (number)
29044      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.
29045      o stacked (boolean) whether or not to tread values as in a stacked bar chart
29046      o to
29047      o stretch (boolean)
29048      o }
29049  -opts (object) options for the pie
29050      o{
29051      o cut
29052      o startAngle (number)
29053      o endAngle (number)
29054      } 
29055  *
29056  * @constructor
29057  * Create a new Input
29058  * @param {Object} config The config object
29059  */
29060
29061 Roo.bootstrap.Graph = function(config){
29062     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29063     
29064     this.addEvents({
29065         // img events
29066         /**
29067          * @event click
29068          * The img click event for the img.
29069          * @param {Roo.EventObject} e
29070          */
29071         "click" : true
29072     });
29073 };
29074
29075 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
29076     
29077     sm: 4,
29078     md: 5,
29079     graphtype: 'bar',
29080     g_height: 250,
29081     g_width: 400,
29082     g_x: 50,
29083     g_y: 50,
29084     g_r: 30,
29085     opts:{
29086         //g_colors: this.colors,
29087         g_type: 'soft',
29088         g_gutter: '20%'
29089
29090     },
29091     title : false,
29092
29093     getAutoCreate : function(){
29094         
29095         var cfg = {
29096             tag: 'div',
29097             html : null
29098         };
29099         
29100         
29101         return  cfg;
29102     },
29103
29104     onRender : function(ct,position){
29105         
29106         
29107         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29108         
29109         if (typeof(Raphael) == 'undefined') {
29110             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29111             return;
29112         }
29113         
29114         this.raphael = Raphael(this.el.dom);
29115         
29116                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29117                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29118                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29119                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29120                 /*
29121                 r.text(160, 10, "Single Series Chart").attr(txtattr);
29122                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29123                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29124                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29125                 
29126                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29127                 r.barchart(330, 10, 300, 220, data1);
29128                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29129                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29130                 */
29131                 
29132                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29133                 // r.barchart(30, 30, 560, 250,  xdata, {
29134                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29135                 //     axis : "0 0 1 1",
29136                 //     axisxlabels :  xdata
29137                 //     //yvalues : cols,
29138                    
29139                 // });
29140 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29141 //        
29142 //        this.load(null,xdata,{
29143 //                axis : "0 0 1 1",
29144 //                axisxlabels :  xdata
29145 //                });
29146
29147     },
29148
29149     load : function(graphtype,xdata,opts)
29150     {
29151         this.raphael.clear();
29152         if(!graphtype) {
29153             graphtype = this.graphtype;
29154         }
29155         if(!opts){
29156             opts = this.opts;
29157         }
29158         var r = this.raphael,
29159             fin = function () {
29160                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29161             },
29162             fout = function () {
29163                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29164             },
29165             pfin = function() {
29166                 this.sector.stop();
29167                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29168
29169                 if (this.label) {
29170                     this.label[0].stop();
29171                     this.label[0].attr({ r: 7.5 });
29172                     this.label[1].attr({ "font-weight": 800 });
29173                 }
29174             },
29175             pfout = function() {
29176                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29177
29178                 if (this.label) {
29179                     this.label[0].animate({ r: 5 }, 500, "bounce");
29180                     this.label[1].attr({ "font-weight": 400 });
29181                 }
29182             };
29183
29184         switch(graphtype){
29185             case 'bar':
29186                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29187                 break;
29188             case 'hbar':
29189                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29190                 break;
29191             case 'pie':
29192 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
29193 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29194 //            
29195                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29196                 
29197                 break;
29198
29199         }
29200         
29201         if(this.title){
29202             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29203         }
29204         
29205     },
29206     
29207     setTitle: function(o)
29208     {
29209         this.title = o;
29210     },
29211     
29212     initEvents: function() {
29213         
29214         if(!this.href){
29215             this.el.on('click', this.onClick, this);
29216         }
29217     },
29218     
29219     onClick : function(e)
29220     {
29221         Roo.log('img onclick');
29222         this.fireEvent('click', this, e);
29223     }
29224    
29225 });
29226
29227  
29228 /*
29229  * - LGPL
29230  *
29231  * numberBox
29232  * 
29233  */
29234 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29235
29236 /**
29237  * @class Roo.bootstrap.dash.NumberBox
29238  * @extends Roo.bootstrap.Component
29239  * Bootstrap NumberBox class
29240  * @cfg {String} headline Box headline
29241  * @cfg {String} content Box content
29242  * @cfg {String} icon Box icon
29243  * @cfg {String} footer Footer text
29244  * @cfg {String} fhref Footer href
29245  * 
29246  * @constructor
29247  * Create a new NumberBox
29248  * @param {Object} config The config object
29249  */
29250
29251
29252 Roo.bootstrap.dash.NumberBox = function(config){
29253     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29254     
29255 };
29256
29257 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
29258     
29259     headline : '',
29260     content : '',
29261     icon : '',
29262     footer : '',
29263     fhref : '',
29264     ficon : '',
29265     
29266     getAutoCreate : function(){
29267         
29268         var cfg = {
29269             tag : 'div',
29270             cls : 'small-box ',
29271             cn : [
29272                 {
29273                     tag : 'div',
29274                     cls : 'inner',
29275                     cn :[
29276                         {
29277                             tag : 'h3',
29278                             cls : 'roo-headline',
29279                             html : this.headline
29280                         },
29281                         {
29282                             tag : 'p',
29283                             cls : 'roo-content',
29284                             html : this.content
29285                         }
29286                     ]
29287                 }
29288             ]
29289         };
29290         
29291         if(this.icon){
29292             cfg.cn.push({
29293                 tag : 'div',
29294                 cls : 'icon',
29295                 cn :[
29296                     {
29297                         tag : 'i',
29298                         cls : 'ion ' + this.icon
29299                     }
29300                 ]
29301             });
29302         }
29303         
29304         if(this.footer){
29305             var footer = {
29306                 tag : 'a',
29307                 cls : 'small-box-footer',
29308                 href : this.fhref || '#',
29309                 html : this.footer
29310             };
29311             
29312             cfg.cn.push(footer);
29313             
29314         }
29315         
29316         return  cfg;
29317     },
29318
29319     onRender : function(ct,position){
29320         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29321
29322
29323        
29324                 
29325     },
29326
29327     setHeadline: function (value)
29328     {
29329         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29330     },
29331     
29332     setFooter: function (value, href)
29333     {
29334         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29335         
29336         if(href){
29337             this.el.select('a.small-box-footer',true).first().attr('href', href);
29338         }
29339         
29340     },
29341
29342     setContent: function (value)
29343     {
29344         this.el.select('.roo-content',true).first().dom.innerHTML = value;
29345     },
29346
29347     initEvents: function() 
29348     {   
29349         
29350     }
29351     
29352 });
29353
29354  
29355 /*
29356  * - LGPL
29357  *
29358  * TabBox
29359  * 
29360  */
29361 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29362
29363 /**
29364  * @class Roo.bootstrap.dash.TabBox
29365  * @extends Roo.bootstrap.Component
29366  * @children Roo.bootstrap.dash.TabPane
29367  * Bootstrap TabBox class
29368  * @cfg {String} title Title of the TabBox
29369  * @cfg {String} icon Icon of the TabBox
29370  * @cfg {Boolean} showtabs (true|false) show the tabs default true
29371  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29372  * 
29373  * @constructor
29374  * Create a new TabBox
29375  * @param {Object} config The config object
29376  */
29377
29378
29379 Roo.bootstrap.dash.TabBox = function(config){
29380     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29381     this.addEvents({
29382         // raw events
29383         /**
29384          * @event addpane
29385          * When a pane is added
29386          * @param {Roo.bootstrap.dash.TabPane} pane
29387          */
29388         "addpane" : true,
29389         /**
29390          * @event activatepane
29391          * When a pane is activated
29392          * @param {Roo.bootstrap.dash.TabPane} pane
29393          */
29394         "activatepane" : true
29395         
29396          
29397     });
29398     
29399     this.panes = [];
29400 };
29401
29402 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
29403
29404     title : '',
29405     icon : false,
29406     showtabs : true,
29407     tabScrollable : false,
29408     
29409     getChildContainer : function()
29410     {
29411         return this.el.select('.tab-content', true).first();
29412     },
29413     
29414     getAutoCreate : function(){
29415         
29416         var header = {
29417             tag: 'li',
29418             cls: 'pull-left header',
29419             html: this.title,
29420             cn : []
29421         };
29422         
29423         if(this.icon){
29424             header.cn.push({
29425                 tag: 'i',
29426                 cls: 'fa ' + this.icon
29427             });
29428         }
29429         
29430         var h = {
29431             tag: 'ul',
29432             cls: 'nav nav-tabs pull-right',
29433             cn: [
29434                 header
29435             ]
29436         };
29437         
29438         if(this.tabScrollable){
29439             h = {
29440                 tag: 'div',
29441                 cls: 'tab-header',
29442                 cn: [
29443                     {
29444                         tag: 'ul',
29445                         cls: 'nav nav-tabs pull-right',
29446                         cn: [
29447                             header
29448                         ]
29449                     }
29450                 ]
29451             };
29452         }
29453         
29454         var cfg = {
29455             tag: 'div',
29456             cls: 'nav-tabs-custom',
29457             cn: [
29458                 h,
29459                 {
29460                     tag: 'div',
29461                     cls: 'tab-content no-padding',
29462                     cn: []
29463                 }
29464             ]
29465         };
29466
29467         return  cfg;
29468     },
29469     initEvents : function()
29470     {
29471         //Roo.log('add add pane handler');
29472         this.on('addpane', this.onAddPane, this);
29473     },
29474      /**
29475      * Updates the box title
29476      * @param {String} html to set the title to.
29477      */
29478     setTitle : function(value)
29479     {
29480         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29481     },
29482     onAddPane : function(pane)
29483     {
29484         this.panes.push(pane);
29485         //Roo.log('addpane');
29486         //Roo.log(pane);
29487         // tabs are rendere left to right..
29488         if(!this.showtabs){
29489             return;
29490         }
29491         
29492         var ctr = this.el.select('.nav-tabs', true).first();
29493          
29494          
29495         var existing = ctr.select('.nav-tab',true);
29496         var qty = existing.getCount();;
29497         
29498         
29499         var tab = ctr.createChild({
29500             tag : 'li',
29501             cls : 'nav-tab' + (qty ? '' : ' active'),
29502             cn : [
29503                 {
29504                     tag : 'a',
29505                     href:'#',
29506                     html : pane.title
29507                 }
29508             ]
29509         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29510         pane.tab = tab;
29511         
29512         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29513         if (!qty) {
29514             pane.el.addClass('active');
29515         }
29516         
29517                 
29518     },
29519     onTabClick : function(ev,un,ob,pane)
29520     {
29521         //Roo.log('tab - prev default');
29522         ev.preventDefault();
29523         
29524         
29525         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29526         pane.tab.addClass('active');
29527         //Roo.log(pane.title);
29528         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29529         // technically we should have a deactivate event.. but maybe add later.
29530         // and it should not de-activate the selected tab...
29531         this.fireEvent('activatepane', pane);
29532         pane.el.addClass('active');
29533         pane.fireEvent('activate');
29534         
29535         
29536     },
29537     
29538     getActivePane : function()
29539     {
29540         var r = false;
29541         Roo.each(this.panes, function(p) {
29542             if(p.el.hasClass('active')){
29543                 r = p;
29544                 return false;
29545             }
29546             
29547             return;
29548         });
29549         
29550         return r;
29551     }
29552     
29553     
29554 });
29555
29556  
29557 /*
29558  * - LGPL
29559  *
29560  * Tab pane
29561  * 
29562  */
29563 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29564 /**
29565  * @class Roo.bootstrap.TabPane
29566  * @extends Roo.bootstrap.Component
29567  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
29568  * Bootstrap TabPane class
29569  * @cfg {Boolean} active (false | true) Default false
29570  * @cfg {String} title title of panel
29571
29572  * 
29573  * @constructor
29574  * Create a new TabPane
29575  * @param {Object} config The config object
29576  */
29577
29578 Roo.bootstrap.dash.TabPane = function(config){
29579     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29580     
29581     this.addEvents({
29582         // raw events
29583         /**
29584          * @event activate
29585          * When a pane is activated
29586          * @param {Roo.bootstrap.dash.TabPane} pane
29587          */
29588         "activate" : true
29589          
29590     });
29591 };
29592
29593 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29594     
29595     active : false,
29596     title : '',
29597     
29598     // the tabBox that this is attached to.
29599     tab : false,
29600      
29601     getAutoCreate : function() 
29602     {
29603         var cfg = {
29604             tag: 'div',
29605             cls: 'tab-pane'
29606         };
29607         
29608         if(this.active){
29609             cfg.cls += ' active';
29610         }
29611         
29612         return cfg;
29613     },
29614     initEvents  : function()
29615     {
29616         //Roo.log('trigger add pane handler');
29617         this.parent().fireEvent('addpane', this)
29618     },
29619     
29620      /**
29621      * Updates the tab title 
29622      * @param {String} html to set the title to.
29623      */
29624     setTitle: function(str)
29625     {
29626         if (!this.tab) {
29627             return;
29628         }
29629         this.title = str;
29630         this.tab.select('a', true).first().dom.innerHTML = str;
29631         
29632     }
29633     
29634     
29635     
29636 });
29637
29638  
29639
29640
29641  /*
29642  * - LGPL
29643  *
29644  * Tooltip
29645  * 
29646  */
29647
29648 /**
29649  * @class Roo.bootstrap.Tooltip
29650  * Bootstrap Tooltip class
29651  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29652  * to determine which dom element triggers the tooltip.
29653  * 
29654  * It needs to add support for additional attributes like tooltip-position
29655  * 
29656  * @constructor
29657  * Create a new Toolti
29658  * @param {Object} config The config object
29659  */
29660
29661 Roo.bootstrap.Tooltip = function(config){
29662     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29663     
29664     this.alignment = Roo.bootstrap.Tooltip.alignment;
29665     
29666     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29667         this.alignment = config.alignment;
29668     }
29669     
29670 };
29671
29672 Roo.apply(Roo.bootstrap.Tooltip, {
29673     /**
29674      * @function init initialize tooltip monitoring.
29675      * @static
29676      */
29677     currentEl : false,
29678     currentTip : false,
29679     currentRegion : false,
29680     
29681     //  init : delay?
29682     
29683     init : function()
29684     {
29685         Roo.get(document).on('mouseover', this.enter ,this);
29686         Roo.get(document).on('mouseout', this.leave, this);
29687          
29688         
29689         this.currentTip = new Roo.bootstrap.Tooltip();
29690     },
29691     
29692     enter : function(ev)
29693     {
29694         var dom = ev.getTarget();
29695         
29696         //Roo.log(['enter',dom]);
29697         var el = Roo.fly(dom);
29698         if (this.currentEl) {
29699             //Roo.log(dom);
29700             //Roo.log(this.currentEl);
29701             //Roo.log(this.currentEl.contains(dom));
29702             if (this.currentEl == el) {
29703                 return;
29704             }
29705             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29706                 return;
29707             }
29708
29709         }
29710         
29711         if (this.currentTip.el) {
29712             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29713         }    
29714         //Roo.log(ev);
29715         
29716         if(!el || el.dom == document){
29717             return;
29718         }
29719         
29720         var bindEl = el; 
29721         var pel = false;
29722         if (!el.attr('tooltip')) {
29723             pel = el.findParent("[tooltip]");
29724             if (pel) {
29725                 bindEl = Roo.get(pel);
29726             }
29727         }
29728         
29729        
29730         
29731         // you can not look for children, as if el is the body.. then everythign is the child..
29732         if (!pel && !el.attr('tooltip')) { //
29733             if (!el.select("[tooltip]").elements.length) {
29734                 return;
29735             }
29736             // is the mouse over this child...?
29737             bindEl = el.select("[tooltip]").first();
29738             var xy = ev.getXY();
29739             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29740                 //Roo.log("not in region.");
29741                 return;
29742             }
29743             //Roo.log("child element over..");
29744             
29745         }
29746         this.currentEl = el;
29747         this.currentTip.bind(bindEl);
29748         this.currentRegion = Roo.lib.Region.getRegion(dom);
29749         this.currentTip.enter();
29750         
29751     },
29752     leave : function(ev)
29753     {
29754         var dom = ev.getTarget();
29755         //Roo.log(['leave',dom]);
29756         if (!this.currentEl) {
29757             return;
29758         }
29759         
29760         
29761         if (dom != this.currentEl.dom) {
29762             return;
29763         }
29764         var xy = ev.getXY();
29765         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29766             return;
29767         }
29768         // only activate leave if mouse cursor is outside... bounding box..
29769         
29770         
29771         
29772         
29773         if (this.currentTip) {
29774             this.currentTip.leave();
29775         }
29776         //Roo.log('clear currentEl');
29777         this.currentEl = false;
29778         
29779         
29780     },
29781     alignment : {
29782         'left' : ['r-l', [-2,0], 'right'],
29783         'right' : ['l-r', [2,0], 'left'],
29784         'bottom' : ['t-b', [0,2], 'top'],
29785         'top' : [ 'b-t', [0,-2], 'bottom']
29786     }
29787     
29788 });
29789
29790
29791 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29792     
29793     
29794     bindEl : false,
29795     
29796     delay : null, // can be { show : 300 , hide: 500}
29797     
29798     timeout : null,
29799     
29800     hoverState : null, //???
29801     
29802     placement : 'bottom', 
29803     
29804     alignment : false,
29805     
29806     getAutoCreate : function(){
29807     
29808         var cfg = {
29809            cls : 'tooltip',   
29810            role : 'tooltip',
29811            cn : [
29812                 {
29813                     cls : 'tooltip-arrow arrow'
29814                 },
29815                 {
29816                     cls : 'tooltip-inner'
29817                 }
29818            ]
29819         };
29820         
29821         return cfg;
29822     },
29823     bind : function(el)
29824     {
29825         this.bindEl = el;
29826     },
29827     
29828     initEvents : function()
29829     {
29830         this.arrowEl = this.el.select('.arrow', true).first();
29831         this.innerEl = this.el.select('.tooltip-inner', true).first();
29832     },
29833     
29834     enter : function () {
29835        
29836         if (this.timeout != null) {
29837             clearTimeout(this.timeout);
29838         }
29839         
29840         this.hoverState = 'in';
29841          //Roo.log("enter - show");
29842         if (!this.delay || !this.delay.show) {
29843             this.show();
29844             return;
29845         }
29846         var _t = this;
29847         this.timeout = setTimeout(function () {
29848             if (_t.hoverState == 'in') {
29849                 _t.show();
29850             }
29851         }, this.delay.show);
29852     },
29853     leave : function()
29854     {
29855         clearTimeout(this.timeout);
29856     
29857         this.hoverState = 'out';
29858          if (!this.delay || !this.delay.hide) {
29859             this.hide();
29860             return;
29861         }
29862        
29863         var _t = this;
29864         this.timeout = setTimeout(function () {
29865             //Roo.log("leave - timeout");
29866             
29867             if (_t.hoverState == 'out') {
29868                 _t.hide();
29869                 Roo.bootstrap.Tooltip.currentEl = false;
29870             }
29871         }, delay);
29872     },
29873     
29874     show : function (msg)
29875     {
29876         if (!this.el) {
29877             this.render(document.body);
29878         }
29879         // set content.
29880         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29881         
29882         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29883         
29884         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29885         
29886         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29887                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29888         
29889         var placement = typeof this.placement == 'function' ?
29890             this.placement.call(this, this.el, on_el) :
29891             this.placement;
29892             
29893         var autoToken = /\s?auto?\s?/i;
29894         var autoPlace = autoToken.test(placement);
29895         if (autoPlace) {
29896             placement = placement.replace(autoToken, '') || 'top';
29897         }
29898         
29899         //this.el.detach()
29900         //this.el.setXY([0,0]);
29901         this.el.show();
29902         //this.el.dom.style.display='block';
29903         
29904         //this.el.appendTo(on_el);
29905         
29906         var p = this.getPosition();
29907         var box = this.el.getBox();
29908         
29909         if (autoPlace) {
29910             // fixme..
29911         }
29912         
29913         var align = this.alignment[placement];
29914         
29915         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29916         
29917         if(placement == 'top' || placement == 'bottom'){
29918             if(xy[0] < 0){
29919                 placement = 'right';
29920             }
29921             
29922             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29923                 placement = 'left';
29924             }
29925             
29926             var scroll = Roo.select('body', true).first().getScroll();
29927             
29928             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29929                 placement = 'top';
29930             }
29931             
29932             align = this.alignment[placement];
29933             
29934             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29935             
29936         }
29937         
29938         var elems = document.getElementsByTagName('div');
29939         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29940         for (var i = 0; i < elems.length; i++) {
29941           var zindex = Number.parseInt(
29942                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29943                 10
29944           );
29945           if (zindex > highest) {
29946             highest = zindex;
29947           }
29948         }
29949         
29950         
29951         
29952         this.el.dom.style.zIndex = highest;
29953         
29954         this.el.alignTo(this.bindEl, align[0],align[1]);
29955         //var arrow = this.el.select('.arrow',true).first();
29956         //arrow.set(align[2], 
29957         
29958         this.el.addClass(placement);
29959         this.el.addClass("bs-tooltip-"+ placement);
29960         
29961         this.el.addClass('in fade show');
29962         
29963         this.hoverState = null;
29964         
29965         if (this.el.hasClass('fade')) {
29966             // fade it?
29967         }
29968         
29969         
29970         
29971         
29972         
29973     },
29974     hide : function()
29975     {
29976          
29977         if (!this.el) {
29978             return;
29979         }
29980         //this.el.setXY([0,0]);
29981         this.el.removeClass(['show', 'in']);
29982         //this.el.hide();
29983         
29984     }
29985     
29986 });
29987  
29988
29989  /*
29990  * - LGPL
29991  *
29992  * Location Picker
29993  * 
29994  */
29995
29996 /**
29997  * @class Roo.bootstrap.LocationPicker
29998  * @extends Roo.bootstrap.Component
29999  * Bootstrap LocationPicker class
30000  * @cfg {Number} latitude Position when init default 0
30001  * @cfg {Number} longitude Position when init default 0
30002  * @cfg {Number} zoom default 15
30003  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
30004  * @cfg {Boolean} mapTypeControl default false
30005  * @cfg {Boolean} disableDoubleClickZoom default false
30006  * @cfg {Boolean} scrollwheel default true
30007  * @cfg {Boolean} streetViewControl default false
30008  * @cfg {Number} radius default 0
30009  * @cfg {String} locationName
30010  * @cfg {Boolean} draggable default true
30011  * @cfg {Boolean} enableAutocomplete default false
30012  * @cfg {Boolean} enableReverseGeocode default true
30013  * @cfg {String} markerTitle
30014  * 
30015  * @constructor
30016  * Create a new LocationPicker
30017  * @param {Object} config The config object
30018  */
30019
30020
30021 Roo.bootstrap.LocationPicker = function(config){
30022     
30023     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30024     
30025     this.addEvents({
30026         /**
30027          * @event initial
30028          * Fires when the picker initialized.
30029          * @param {Roo.bootstrap.LocationPicker} this
30030          * @param {Google Location} location
30031          */
30032         initial : true,
30033         /**
30034          * @event positionchanged
30035          * Fires when the picker position changed.
30036          * @param {Roo.bootstrap.LocationPicker} this
30037          * @param {Google Location} location
30038          */
30039         positionchanged : true,
30040         /**
30041          * @event resize
30042          * Fires when the map resize.
30043          * @param {Roo.bootstrap.LocationPicker} this
30044          */
30045         resize : true,
30046         /**
30047          * @event show
30048          * Fires when the map show.
30049          * @param {Roo.bootstrap.LocationPicker} this
30050          */
30051         show : true,
30052         /**
30053          * @event hide
30054          * Fires when the map hide.
30055          * @param {Roo.bootstrap.LocationPicker} this
30056          */
30057         hide : true,
30058         /**
30059          * @event mapClick
30060          * Fires when click the map.
30061          * @param {Roo.bootstrap.LocationPicker} this
30062          * @param {Map event} e
30063          */
30064         mapClick : true,
30065         /**
30066          * @event mapRightClick
30067          * Fires when right click the map.
30068          * @param {Roo.bootstrap.LocationPicker} this
30069          * @param {Map event} e
30070          */
30071         mapRightClick : true,
30072         /**
30073          * @event markerClick
30074          * Fires when click the marker.
30075          * @param {Roo.bootstrap.LocationPicker} this
30076          * @param {Map event} e
30077          */
30078         markerClick : true,
30079         /**
30080          * @event markerRightClick
30081          * Fires when right click the marker.
30082          * @param {Roo.bootstrap.LocationPicker} this
30083          * @param {Map event} e
30084          */
30085         markerRightClick : true,
30086         /**
30087          * @event OverlayViewDraw
30088          * Fires when OverlayView Draw
30089          * @param {Roo.bootstrap.LocationPicker} this
30090          */
30091         OverlayViewDraw : true,
30092         /**
30093          * @event OverlayViewOnAdd
30094          * Fires when OverlayView Draw
30095          * @param {Roo.bootstrap.LocationPicker} this
30096          */
30097         OverlayViewOnAdd : true,
30098         /**
30099          * @event OverlayViewOnRemove
30100          * Fires when OverlayView Draw
30101          * @param {Roo.bootstrap.LocationPicker} this
30102          */
30103         OverlayViewOnRemove : true,
30104         /**
30105          * @event OverlayViewShow
30106          * Fires when OverlayView Draw
30107          * @param {Roo.bootstrap.LocationPicker} this
30108          * @param {Pixel} cpx
30109          */
30110         OverlayViewShow : true,
30111         /**
30112          * @event OverlayViewHide
30113          * Fires when OverlayView Draw
30114          * @param {Roo.bootstrap.LocationPicker} this
30115          */
30116         OverlayViewHide : true,
30117         /**
30118          * @event loadexception
30119          * Fires when load google lib failed.
30120          * @param {Roo.bootstrap.LocationPicker} this
30121          */
30122         loadexception : true
30123     });
30124         
30125 };
30126
30127 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30128     
30129     gMapContext: false,
30130     
30131     latitude: 0,
30132     longitude: 0,
30133     zoom: 15,
30134     mapTypeId: false,
30135     mapTypeControl: false,
30136     disableDoubleClickZoom: false,
30137     scrollwheel: true,
30138     streetViewControl: false,
30139     radius: 0,
30140     locationName: '',
30141     draggable: true,
30142     enableAutocomplete: false,
30143     enableReverseGeocode: true,
30144     markerTitle: '',
30145     
30146     getAutoCreate: function()
30147     {
30148
30149         var cfg = {
30150             tag: 'div',
30151             cls: 'roo-location-picker'
30152         };
30153         
30154         return cfg
30155     },
30156     
30157     initEvents: function(ct, position)
30158     {       
30159         if(!this.el.getWidth() || this.isApplied()){
30160             return;
30161         }
30162         
30163         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30164         
30165         this.initial();
30166     },
30167     
30168     initial: function()
30169     {
30170         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30171             this.fireEvent('loadexception', this);
30172             return;
30173         }
30174         
30175         if(!this.mapTypeId){
30176             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30177         }
30178         
30179         this.gMapContext = this.GMapContext();
30180         
30181         this.initOverlayView();
30182         
30183         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30184         
30185         var _this = this;
30186                 
30187         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30188             _this.setPosition(_this.gMapContext.marker.position);
30189         });
30190         
30191         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30192             _this.fireEvent('mapClick', this, event);
30193             
30194         });
30195
30196         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30197             _this.fireEvent('mapRightClick', this, event);
30198             
30199         });
30200         
30201         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30202             _this.fireEvent('markerClick', this, event);
30203             
30204         });
30205
30206         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30207             _this.fireEvent('markerRightClick', this, event);
30208             
30209         });
30210         
30211         this.setPosition(this.gMapContext.location);
30212         
30213         this.fireEvent('initial', this, this.gMapContext.location);
30214     },
30215     
30216     initOverlayView: function()
30217     {
30218         var _this = this;
30219         
30220         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30221             
30222             draw: function()
30223             {
30224                 _this.fireEvent('OverlayViewDraw', _this);
30225             },
30226             
30227             onAdd: function()
30228             {
30229                 _this.fireEvent('OverlayViewOnAdd', _this);
30230             },
30231             
30232             onRemove: function()
30233             {
30234                 _this.fireEvent('OverlayViewOnRemove', _this);
30235             },
30236             
30237             show: function(cpx)
30238             {
30239                 _this.fireEvent('OverlayViewShow', _this, cpx);
30240             },
30241             
30242             hide: function()
30243             {
30244                 _this.fireEvent('OverlayViewHide', _this);
30245             }
30246             
30247         });
30248     },
30249     
30250     fromLatLngToContainerPixel: function(event)
30251     {
30252         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30253     },
30254     
30255     isApplied: function() 
30256     {
30257         return this.getGmapContext() == false ? false : true;
30258     },
30259     
30260     getGmapContext: function() 
30261     {
30262         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30263     },
30264     
30265     GMapContext: function() 
30266     {
30267         var position = new google.maps.LatLng(this.latitude, this.longitude);
30268         
30269         var _map = new google.maps.Map(this.el.dom, {
30270             center: position,
30271             zoom: this.zoom,
30272             mapTypeId: this.mapTypeId,
30273             mapTypeControl: this.mapTypeControl,
30274             disableDoubleClickZoom: this.disableDoubleClickZoom,
30275             scrollwheel: this.scrollwheel,
30276             streetViewControl: this.streetViewControl,
30277             locationName: this.locationName,
30278             draggable: this.draggable,
30279             enableAutocomplete: this.enableAutocomplete,
30280             enableReverseGeocode: this.enableReverseGeocode
30281         });
30282         
30283         var _marker = new google.maps.Marker({
30284             position: position,
30285             map: _map,
30286             title: this.markerTitle,
30287             draggable: this.draggable
30288         });
30289         
30290         return {
30291             map: _map,
30292             marker: _marker,
30293             circle: null,
30294             location: position,
30295             radius: this.radius,
30296             locationName: this.locationName,
30297             addressComponents: {
30298                 formatted_address: null,
30299                 addressLine1: null,
30300                 addressLine2: null,
30301                 streetName: null,
30302                 streetNumber: null,
30303                 city: null,
30304                 district: null,
30305                 state: null,
30306                 stateOrProvince: null
30307             },
30308             settings: this,
30309             domContainer: this.el.dom,
30310             geodecoder: new google.maps.Geocoder()
30311         };
30312     },
30313     
30314     drawCircle: function(center, radius, options) 
30315     {
30316         if (this.gMapContext.circle != null) {
30317             this.gMapContext.circle.setMap(null);
30318         }
30319         if (radius > 0) {
30320             radius *= 1;
30321             options = Roo.apply({}, options, {
30322                 strokeColor: "#0000FF",
30323                 strokeOpacity: .35,
30324                 strokeWeight: 2,
30325                 fillColor: "#0000FF",
30326                 fillOpacity: .2
30327             });
30328             
30329             options.map = this.gMapContext.map;
30330             options.radius = radius;
30331             options.center = center;
30332             this.gMapContext.circle = new google.maps.Circle(options);
30333             return this.gMapContext.circle;
30334         }
30335         
30336         return null;
30337     },
30338     
30339     setPosition: function(location) 
30340     {
30341         this.gMapContext.location = location;
30342         this.gMapContext.marker.setPosition(location);
30343         this.gMapContext.map.panTo(location);
30344         this.drawCircle(location, this.gMapContext.radius, {});
30345         
30346         var _this = this;
30347         
30348         if (this.gMapContext.settings.enableReverseGeocode) {
30349             this.gMapContext.geodecoder.geocode({
30350                 latLng: this.gMapContext.location
30351             }, function(results, status) {
30352                 
30353                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30354                     _this.gMapContext.locationName = results[0].formatted_address;
30355                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30356                     
30357                     _this.fireEvent('positionchanged', this, location);
30358                 }
30359             });
30360             
30361             return;
30362         }
30363         
30364         this.fireEvent('positionchanged', this, location);
30365     },
30366     
30367     resize: function()
30368     {
30369         google.maps.event.trigger(this.gMapContext.map, "resize");
30370         
30371         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30372         
30373         this.fireEvent('resize', this);
30374     },
30375     
30376     setPositionByLatLng: function(latitude, longitude)
30377     {
30378         this.setPosition(new google.maps.LatLng(latitude, longitude));
30379     },
30380     
30381     getCurrentPosition: function() 
30382     {
30383         return {
30384             latitude: this.gMapContext.location.lat(),
30385             longitude: this.gMapContext.location.lng()
30386         };
30387     },
30388     
30389     getAddressName: function() 
30390     {
30391         return this.gMapContext.locationName;
30392     },
30393     
30394     getAddressComponents: function() 
30395     {
30396         return this.gMapContext.addressComponents;
30397     },
30398     
30399     address_component_from_google_geocode: function(address_components) 
30400     {
30401         var result = {};
30402         
30403         for (var i = 0; i < address_components.length; i++) {
30404             var component = address_components[i];
30405             if (component.types.indexOf("postal_code") >= 0) {
30406                 result.postalCode = component.short_name;
30407             } else if (component.types.indexOf("street_number") >= 0) {
30408                 result.streetNumber = component.short_name;
30409             } else if (component.types.indexOf("route") >= 0) {
30410                 result.streetName = component.short_name;
30411             } else if (component.types.indexOf("neighborhood") >= 0) {
30412                 result.city = component.short_name;
30413             } else if (component.types.indexOf("locality") >= 0) {
30414                 result.city = component.short_name;
30415             } else if (component.types.indexOf("sublocality") >= 0) {
30416                 result.district = component.short_name;
30417             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30418                 result.stateOrProvince = component.short_name;
30419             } else if (component.types.indexOf("country") >= 0) {
30420                 result.country = component.short_name;
30421             }
30422         }
30423         
30424         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30425         result.addressLine2 = "";
30426         return result;
30427     },
30428     
30429     setZoomLevel: function(zoom)
30430     {
30431         this.gMapContext.map.setZoom(zoom);
30432     },
30433     
30434     show: function()
30435     {
30436         if(!this.el){
30437             return;
30438         }
30439         
30440         this.el.show();
30441         
30442         this.resize();
30443         
30444         this.fireEvent('show', this);
30445     },
30446     
30447     hide: function()
30448     {
30449         if(!this.el){
30450             return;
30451         }
30452         
30453         this.el.hide();
30454         
30455         this.fireEvent('hide', this);
30456     }
30457     
30458 });
30459
30460 Roo.apply(Roo.bootstrap.LocationPicker, {
30461     
30462     OverlayView : function(map, options)
30463     {
30464         options = options || {};
30465         
30466         this.setMap(map);
30467     }
30468     
30469     
30470 });/**
30471  * @class Roo.bootstrap.Alert
30472  * @extends Roo.bootstrap.Component
30473  * Bootstrap Alert class - shows an alert area box
30474  * eg
30475  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30476   Enter a valid email address
30477 </div>
30478  * @licence LGPL
30479  * @cfg {String} title The title of alert
30480  * @cfg {String} html The content of alert
30481  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30482  * @cfg {String} fa font-awesomeicon
30483  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30484  * @cfg {Boolean} close true to show a x closer
30485  * 
30486  * 
30487  * @constructor
30488  * Create a new alert
30489  * @param {Object} config The config object
30490  */
30491
30492
30493 Roo.bootstrap.Alert = function(config){
30494     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30495     
30496 };
30497
30498 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30499     
30500     title: '',
30501     html: '',
30502     weight: false,
30503     fa: false,
30504     faicon: false, // BC
30505     close : false,
30506     
30507     
30508     getAutoCreate : function()
30509     {
30510         
30511         var cfg = {
30512             tag : 'div',
30513             cls : 'alert',
30514             cn : [
30515                 {
30516                     tag: 'button',
30517                     type :  "button",
30518                     cls: "close",
30519                     html : '×',
30520                     style : this.close ? '' : 'display:none'
30521                 },
30522                 {
30523                     tag : 'i',
30524                     cls : 'roo-alert-icon'
30525                     
30526                 },
30527                 {
30528                     tag : 'b',
30529                     cls : 'roo-alert-title',
30530                     html : this.title
30531                 },
30532                 {
30533                     tag : 'span',
30534                     cls : 'roo-alert-text',
30535                     html : this.html
30536                 }
30537             ]
30538         };
30539         
30540         if(this.faicon){
30541             cfg.cn[0].cls += ' fa ' + this.faicon;
30542         }
30543         if(this.fa){
30544             cfg.cn[0].cls += ' fa ' + this.fa;
30545         }
30546         
30547         if(this.weight){
30548             cfg.cls += ' alert-' + this.weight;
30549         }
30550         
30551         return cfg;
30552     },
30553     
30554     initEvents: function() 
30555     {
30556         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30557         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30558         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30559         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30560         if (this.seconds > 0) {
30561             this.hide.defer(this.seconds, this);
30562         }
30563     },
30564     /**
30565      * Set the Title Message HTML
30566      * @param {String} html
30567      */
30568     setTitle : function(str)
30569     {
30570         this.titleEl.dom.innerHTML = str;
30571     },
30572      
30573      /**
30574      * Set the Body Message HTML
30575      * @param {String} html
30576      */
30577     setHtml : function(str)
30578     {
30579         this.htmlEl.dom.innerHTML = str;
30580     },
30581     /**
30582      * Set the Weight of the alert
30583      * @param {String} (success|info|warning|danger) weight
30584      */
30585     
30586     setWeight : function(weight)
30587     {
30588         if(this.weight){
30589             this.el.removeClass('alert-' + this.weight);
30590         }
30591         
30592         this.weight = weight;
30593         
30594         this.el.addClass('alert-' + this.weight);
30595     },
30596       /**
30597      * Set the Icon of the alert
30598      * @param {String} see fontawsome names (name without the 'fa-' bit)
30599      */
30600     setIcon : function(icon)
30601     {
30602         if(this.faicon){
30603             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30604         }
30605         
30606         this.faicon = icon;
30607         
30608         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30609     },
30610     /**
30611      * Hide the Alert
30612      */
30613     hide: function() 
30614     {
30615         this.el.hide();   
30616     },
30617     /**
30618      * Show the Alert
30619      */
30620     show: function() 
30621     {  
30622         this.el.show();   
30623     }
30624     
30625 });
30626
30627  
30628 /*
30629 * Licence: LGPL
30630 */
30631
30632 /**
30633  * @class Roo.bootstrap.UploadCropbox
30634  * @extends Roo.bootstrap.Component
30635  * Bootstrap UploadCropbox class
30636  * @cfg {String} emptyText show when image has been loaded
30637  * @cfg {String} rotateNotify show when image too small to rotate
30638  * @cfg {Number} errorTimeout default 3000
30639  * @cfg {Number} minWidth default 300
30640  * @cfg {Number} minHeight default 300
30641  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30642  * @cfg {Boolean} isDocument (true|false) default false
30643  * @cfg {String} url action url
30644  * @cfg {String} paramName default 'imageUpload'
30645  * @cfg {String} method default POST
30646  * @cfg {Boolean} loadMask (true|false) default true
30647  * @cfg {Boolean} loadingText default 'Loading...'
30648  * 
30649  * @constructor
30650  * Create a new UploadCropbox
30651  * @param {Object} config The config object
30652  */
30653
30654 Roo.bootstrap.UploadCropbox = function(config){
30655     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30656     
30657     this.addEvents({
30658         /**
30659          * @event beforeselectfile
30660          * Fire before select file
30661          * @param {Roo.bootstrap.UploadCropbox} this
30662          */
30663         "beforeselectfile" : true,
30664         /**
30665          * @event initial
30666          * Fire after initEvent
30667          * @param {Roo.bootstrap.UploadCropbox} this
30668          */
30669         "initial" : true,
30670         /**
30671          * @event crop
30672          * Fire after initEvent
30673          * @param {Roo.bootstrap.UploadCropbox} this
30674          * @param {String} data
30675          */
30676         "crop" : true,
30677         /**
30678          * @event prepare
30679          * Fire when preparing the file data
30680          * @param {Roo.bootstrap.UploadCropbox} this
30681          * @param {Object} file
30682          */
30683         "prepare" : true,
30684         /**
30685          * @event exception
30686          * Fire when get exception
30687          * @param {Roo.bootstrap.UploadCropbox} this
30688          * @param {XMLHttpRequest} xhr
30689          */
30690         "exception" : true,
30691         /**
30692          * @event beforeloadcanvas
30693          * Fire before load the canvas
30694          * @param {Roo.bootstrap.UploadCropbox} this
30695          * @param {String} src
30696          */
30697         "beforeloadcanvas" : true,
30698         /**
30699          * @event trash
30700          * Fire when trash image
30701          * @param {Roo.bootstrap.UploadCropbox} this
30702          */
30703         "trash" : true,
30704         /**
30705          * @event download
30706          * Fire when download the image
30707          * @param {Roo.bootstrap.UploadCropbox} this
30708          */
30709         "download" : true,
30710         /**
30711          * @event footerbuttonclick
30712          * Fire when footerbuttonclick
30713          * @param {Roo.bootstrap.UploadCropbox} this
30714          * @param {String} type
30715          */
30716         "footerbuttonclick" : true,
30717         /**
30718          * @event resize
30719          * Fire when resize
30720          * @param {Roo.bootstrap.UploadCropbox} this
30721          */
30722         "resize" : true,
30723         /**
30724          * @event rotate
30725          * Fire when rotate the image
30726          * @param {Roo.bootstrap.UploadCropbox} this
30727          * @param {String} pos
30728          */
30729         "rotate" : true,
30730         /**
30731          * @event inspect
30732          * Fire when inspect the file
30733          * @param {Roo.bootstrap.UploadCropbox} this
30734          * @param {Object} file
30735          */
30736         "inspect" : true,
30737         /**
30738          * @event upload
30739          * Fire when xhr upload the file
30740          * @param {Roo.bootstrap.UploadCropbox} this
30741          * @param {Object} data
30742          */
30743         "upload" : true,
30744         /**
30745          * @event arrange
30746          * Fire when arrange the file data
30747          * @param {Roo.bootstrap.UploadCropbox} this
30748          * @param {Object} formData
30749          */
30750         "arrange" : true
30751     });
30752     
30753     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30754 };
30755
30756 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30757     
30758     emptyText : 'Click to upload image',
30759     rotateNotify : 'Image is too small to rotate',
30760     errorTimeout : 3000,
30761     scale : 0,
30762     baseScale : 1,
30763     rotate : 0,
30764     dragable : false,
30765     pinching : false,
30766     mouseX : 0,
30767     mouseY : 0,
30768     cropData : false,
30769     minWidth : 300,
30770     minHeight : 300,
30771     file : false,
30772     exif : {},
30773     baseRotate : 1,
30774     cropType : 'image/jpeg',
30775     buttons : false,
30776     canvasLoaded : false,
30777     isDocument : false,
30778     method : 'POST',
30779     paramName : 'imageUpload',
30780     loadMask : true,
30781     loadingText : 'Loading...',
30782     maskEl : false,
30783     
30784     getAutoCreate : function()
30785     {
30786         var cfg = {
30787             tag : 'div',
30788             cls : 'roo-upload-cropbox',
30789             cn : [
30790                 {
30791                     tag : 'input',
30792                     cls : 'roo-upload-cropbox-selector',
30793                     type : 'file'
30794                 },
30795                 {
30796                     tag : 'div',
30797                     cls : 'roo-upload-cropbox-body',
30798                     style : 'cursor:pointer',
30799                     cn : [
30800                         {
30801                             tag : 'div',
30802                             cls : 'roo-upload-cropbox-preview'
30803                         },
30804                         {
30805                             tag : 'div',
30806                             cls : 'roo-upload-cropbox-thumb'
30807                         },
30808                         {
30809                             tag : 'div',
30810                             cls : 'roo-upload-cropbox-empty-notify',
30811                             html : this.emptyText
30812                         },
30813                         {
30814                             tag : 'div',
30815                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30816                             html : this.rotateNotify
30817                         }
30818                     ]
30819                 },
30820                 {
30821                     tag : 'div',
30822                     cls : 'roo-upload-cropbox-footer',
30823                     cn : {
30824                         tag : 'div',
30825                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30826                         cn : []
30827                     }
30828                 }
30829             ]
30830         };
30831         
30832         return cfg;
30833     },
30834     
30835     onRender : function(ct, position)
30836     {
30837         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30838         
30839         if (this.buttons.length) {
30840             
30841             Roo.each(this.buttons, function(bb) {
30842                 
30843                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30844                 
30845                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30846                 
30847             }, this);
30848         }
30849         
30850         if(this.loadMask){
30851             this.maskEl = this.el;
30852         }
30853     },
30854     
30855     initEvents : function()
30856     {
30857         this.urlAPI = (window.createObjectURL && window) || 
30858                                 (window.URL && URL.revokeObjectURL && URL) || 
30859                                 (window.webkitURL && webkitURL);
30860                         
30861         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30862         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30863         
30864         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30865         this.selectorEl.hide();
30866         
30867         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30868         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30869         
30870         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30871         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30872         this.thumbEl.hide();
30873         
30874         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30875         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30876         
30877         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30878         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30879         this.errorEl.hide();
30880         
30881         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30882         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30883         this.footerEl.hide();
30884         
30885         this.setThumbBoxSize();
30886         
30887         this.bind();
30888         
30889         this.resize();
30890         
30891         this.fireEvent('initial', this);
30892     },
30893
30894     bind : function()
30895     {
30896         var _this = this;
30897         
30898         window.addEventListener("resize", function() { _this.resize(); } );
30899         
30900         this.bodyEl.on('click', this.beforeSelectFile, this);
30901         
30902         if(Roo.isTouch){
30903             this.bodyEl.on('touchstart', this.onTouchStart, this);
30904             this.bodyEl.on('touchmove', this.onTouchMove, this);
30905             this.bodyEl.on('touchend', this.onTouchEnd, this);
30906         }
30907         
30908         if(!Roo.isTouch){
30909             this.bodyEl.on('mousedown', this.onMouseDown, this);
30910             this.bodyEl.on('mousemove', this.onMouseMove, this);
30911             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30912             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30913             Roo.get(document).on('mouseup', this.onMouseUp, this);
30914         }
30915         
30916         this.selectorEl.on('change', this.onFileSelected, this);
30917     },
30918     
30919     reset : function()
30920     {    
30921         this.scale = 0;
30922         this.baseScale = 1;
30923         this.rotate = 0;
30924         this.baseRotate = 1;
30925         this.dragable = false;
30926         this.pinching = false;
30927         this.mouseX = 0;
30928         this.mouseY = 0;
30929         this.cropData = false;
30930         this.notifyEl.dom.innerHTML = this.emptyText;
30931         
30932         this.selectorEl.dom.value = '';
30933         
30934     },
30935     
30936     resize : function()
30937     {
30938         if(this.fireEvent('resize', this) != false){
30939             this.setThumbBoxPosition();
30940             this.setCanvasPosition();
30941         }
30942     },
30943     
30944     onFooterButtonClick : function(e, el, o, type)
30945     {
30946         switch (type) {
30947             case 'rotate-left' :
30948                 this.onRotateLeft(e);
30949                 break;
30950             case 'rotate-right' :
30951                 this.onRotateRight(e);
30952                 break;
30953             case 'picture' :
30954                 this.beforeSelectFile(e);
30955                 break;
30956             case 'trash' :
30957                 this.trash(e);
30958                 break;
30959             case 'crop' :
30960                 this.crop(e);
30961                 break;
30962             case 'download' :
30963                 this.download(e);
30964                 break;
30965             default :
30966                 break;
30967         }
30968         
30969         this.fireEvent('footerbuttonclick', this, type);
30970     },
30971     
30972     beforeSelectFile : function(e)
30973     {
30974         e.preventDefault();
30975         
30976         if(this.fireEvent('beforeselectfile', this) != false){
30977             this.selectorEl.dom.click();
30978         }
30979     },
30980     
30981     onFileSelected : function(e)
30982     {
30983         e.preventDefault();
30984         
30985         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30986             return;
30987         }
30988         
30989         var file = this.selectorEl.dom.files[0];
30990         
30991         if(this.fireEvent('inspect', this, file) != false){
30992             this.prepare(file);
30993         }
30994         
30995     },
30996     
30997     trash : function(e)
30998     {
30999         this.fireEvent('trash', this);
31000     },
31001     
31002     download : function(e)
31003     {
31004         this.fireEvent('download', this);
31005     },
31006     
31007     loadCanvas : function(src)
31008     {   
31009         if(this.fireEvent('beforeloadcanvas', this, src) != false){
31010             
31011             this.reset();
31012             
31013             this.imageEl = document.createElement('img');
31014             
31015             var _this = this;
31016             
31017             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31018             
31019             this.imageEl.src = src;
31020         }
31021     },
31022     
31023     onLoadCanvas : function()
31024     {   
31025         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31026         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31027         
31028         this.bodyEl.un('click', this.beforeSelectFile, this);
31029         
31030         this.notifyEl.hide();
31031         this.thumbEl.show();
31032         this.footerEl.show();
31033         
31034         this.baseRotateLevel();
31035         
31036         if(this.isDocument){
31037             this.setThumbBoxSize();
31038         }
31039         
31040         this.setThumbBoxPosition();
31041         
31042         this.baseScaleLevel();
31043         
31044         this.draw();
31045         
31046         this.resize();
31047         
31048         this.canvasLoaded = true;
31049         
31050         if(this.loadMask){
31051             this.maskEl.unmask();
31052         }
31053         
31054     },
31055     
31056     setCanvasPosition : function()
31057     {   
31058         if(!this.canvasEl){
31059             return;
31060         }
31061         
31062         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31063         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31064         
31065         this.previewEl.setLeft(pw);
31066         this.previewEl.setTop(ph);
31067         
31068     },
31069     
31070     onMouseDown : function(e)
31071     {   
31072         e.stopEvent();
31073         
31074         this.dragable = true;
31075         this.pinching = false;
31076         
31077         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31078             this.dragable = false;
31079             return;
31080         }
31081         
31082         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31083         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31084         
31085     },
31086     
31087     onMouseMove : function(e)
31088     {   
31089         e.stopEvent();
31090         
31091         if(!this.canvasLoaded){
31092             return;
31093         }
31094         
31095         if (!this.dragable){
31096             return;
31097         }
31098         
31099         var minX = Math.ceil(this.thumbEl.getLeft(true));
31100         var minY = Math.ceil(this.thumbEl.getTop(true));
31101         
31102         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31103         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31104         
31105         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31106         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31107         
31108         x = x - this.mouseX;
31109         y = y - this.mouseY;
31110         
31111         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31112         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31113         
31114         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31115         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31116         
31117         this.previewEl.setLeft(bgX);
31118         this.previewEl.setTop(bgY);
31119         
31120         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31121         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31122     },
31123     
31124     onMouseUp : function(e)
31125     {   
31126         e.stopEvent();
31127         
31128         this.dragable = false;
31129     },
31130     
31131     onMouseWheel : function(e)
31132     {   
31133         e.stopEvent();
31134         
31135         this.startScale = this.scale;
31136         
31137         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31138         
31139         if(!this.zoomable()){
31140             this.scale = this.startScale;
31141             return;
31142         }
31143         
31144         this.draw();
31145         
31146         return;
31147     },
31148     
31149     zoomable : function()
31150     {
31151         var minScale = this.thumbEl.getWidth() / this.minWidth;
31152         
31153         if(this.minWidth < this.minHeight){
31154             minScale = this.thumbEl.getHeight() / this.minHeight;
31155         }
31156         
31157         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31158         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31159         
31160         if(
31161                 this.isDocument &&
31162                 (this.rotate == 0 || this.rotate == 180) && 
31163                 (
31164                     width > this.imageEl.OriginWidth || 
31165                     height > this.imageEl.OriginHeight ||
31166                     (width < this.minWidth && height < this.minHeight)
31167                 )
31168         ){
31169             return false;
31170         }
31171         
31172         if(
31173                 this.isDocument &&
31174                 (this.rotate == 90 || this.rotate == 270) && 
31175                 (
31176                     width > this.imageEl.OriginWidth || 
31177                     height > this.imageEl.OriginHeight ||
31178                     (width < this.minHeight && height < this.minWidth)
31179                 )
31180         ){
31181             return false;
31182         }
31183         
31184         if(
31185                 !this.isDocument &&
31186                 (this.rotate == 0 || this.rotate == 180) && 
31187                 (
31188                     width < this.minWidth || 
31189                     width > this.imageEl.OriginWidth || 
31190                     height < this.minHeight || 
31191                     height > this.imageEl.OriginHeight
31192                 )
31193         ){
31194             return false;
31195         }
31196         
31197         if(
31198                 !this.isDocument &&
31199                 (this.rotate == 90 || this.rotate == 270) && 
31200                 (
31201                     width < this.minHeight || 
31202                     width > this.imageEl.OriginWidth || 
31203                     height < this.minWidth || 
31204                     height > this.imageEl.OriginHeight
31205                 )
31206         ){
31207             return false;
31208         }
31209         
31210         return true;
31211         
31212     },
31213     
31214     onRotateLeft : function(e)
31215     {   
31216         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31217             
31218             var minScale = this.thumbEl.getWidth() / this.minWidth;
31219             
31220             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31221             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31222             
31223             this.startScale = this.scale;
31224             
31225             while (this.getScaleLevel() < minScale){
31226             
31227                 this.scale = this.scale + 1;
31228                 
31229                 if(!this.zoomable()){
31230                     break;
31231                 }
31232                 
31233                 if(
31234                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31235                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31236                 ){
31237                     continue;
31238                 }
31239                 
31240                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31241
31242                 this.draw();
31243                 
31244                 return;
31245             }
31246             
31247             this.scale = this.startScale;
31248             
31249             this.onRotateFail();
31250             
31251             return false;
31252         }
31253         
31254         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31255
31256         if(this.isDocument){
31257             this.setThumbBoxSize();
31258             this.setThumbBoxPosition();
31259             this.setCanvasPosition();
31260         }
31261         
31262         this.draw();
31263         
31264         this.fireEvent('rotate', this, 'left');
31265         
31266     },
31267     
31268     onRotateRight : function(e)
31269     {
31270         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31271             
31272             var minScale = this.thumbEl.getWidth() / this.minWidth;
31273         
31274             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31275             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31276             
31277             this.startScale = this.scale;
31278             
31279             while (this.getScaleLevel() < minScale){
31280             
31281                 this.scale = this.scale + 1;
31282                 
31283                 if(!this.zoomable()){
31284                     break;
31285                 }
31286                 
31287                 if(
31288                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31289                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31290                 ){
31291                     continue;
31292                 }
31293                 
31294                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31295
31296                 this.draw();
31297                 
31298                 return;
31299             }
31300             
31301             this.scale = this.startScale;
31302             
31303             this.onRotateFail();
31304             
31305             return false;
31306         }
31307         
31308         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31309
31310         if(this.isDocument){
31311             this.setThumbBoxSize();
31312             this.setThumbBoxPosition();
31313             this.setCanvasPosition();
31314         }
31315         
31316         this.draw();
31317         
31318         this.fireEvent('rotate', this, 'right');
31319     },
31320     
31321     onRotateFail : function()
31322     {
31323         this.errorEl.show(true);
31324         
31325         var _this = this;
31326         
31327         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31328     },
31329     
31330     draw : function()
31331     {
31332         this.previewEl.dom.innerHTML = '';
31333         
31334         var canvasEl = document.createElement("canvas");
31335         
31336         var contextEl = canvasEl.getContext("2d");
31337         
31338         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31339         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31340         var center = this.imageEl.OriginWidth / 2;
31341         
31342         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31343             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31344             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31345             center = this.imageEl.OriginHeight / 2;
31346         }
31347         
31348         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31349         
31350         contextEl.translate(center, center);
31351         contextEl.rotate(this.rotate * Math.PI / 180);
31352
31353         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31354         
31355         this.canvasEl = document.createElement("canvas");
31356         
31357         this.contextEl = this.canvasEl.getContext("2d");
31358         
31359         switch (this.rotate) {
31360             case 0 :
31361                 
31362                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31363                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31364                 
31365                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31366                 
31367                 break;
31368             case 90 : 
31369                 
31370                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31371                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31372                 
31373                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31374                     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);
31375                     break;
31376                 }
31377                 
31378                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31379                 
31380                 break;
31381             case 180 :
31382                 
31383                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31384                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31385                 
31386                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31387                     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);
31388                     break;
31389                 }
31390                 
31391                 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);
31392                 
31393                 break;
31394             case 270 :
31395                 
31396                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31397                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31398         
31399                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31400                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31401                     break;
31402                 }
31403                 
31404                 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);
31405                 
31406                 break;
31407             default : 
31408                 break;
31409         }
31410         
31411         this.previewEl.appendChild(this.canvasEl);
31412         
31413         this.setCanvasPosition();
31414     },
31415     
31416     crop : function()
31417     {
31418         if(!this.canvasLoaded){
31419             return;
31420         }
31421         
31422         var imageCanvas = document.createElement("canvas");
31423         
31424         var imageContext = imageCanvas.getContext("2d");
31425         
31426         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31427         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31428         
31429         var center = imageCanvas.width / 2;
31430         
31431         imageContext.translate(center, center);
31432         
31433         imageContext.rotate(this.rotate * Math.PI / 180);
31434         
31435         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31436         
31437         var canvas = document.createElement("canvas");
31438         
31439         var context = canvas.getContext("2d");
31440                 
31441         canvas.width = this.minWidth;
31442         canvas.height = this.minHeight;
31443
31444         switch (this.rotate) {
31445             case 0 :
31446                 
31447                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31448                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31449                 
31450                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31451                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31452                 
31453                 var targetWidth = this.minWidth - 2 * x;
31454                 var targetHeight = this.minHeight - 2 * y;
31455                 
31456                 var scale = 1;
31457                 
31458                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31459                     scale = targetWidth / width;
31460                 }
31461                 
31462                 if(x > 0 && y == 0){
31463                     scale = targetHeight / height;
31464                 }
31465                 
31466                 if(x > 0 && y > 0){
31467                     scale = targetWidth / width;
31468                     
31469                     if(width < height){
31470                         scale = targetHeight / height;
31471                     }
31472                 }
31473                 
31474                 context.scale(scale, scale);
31475                 
31476                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31477                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31478
31479                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31480                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31481
31482                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31483                 
31484                 break;
31485             case 90 : 
31486                 
31487                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31488                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31489                 
31490                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31491                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31492                 
31493                 var targetWidth = this.minWidth - 2 * x;
31494                 var targetHeight = this.minHeight - 2 * y;
31495                 
31496                 var scale = 1;
31497                 
31498                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31499                     scale = targetWidth / width;
31500                 }
31501                 
31502                 if(x > 0 && y == 0){
31503                     scale = targetHeight / height;
31504                 }
31505                 
31506                 if(x > 0 && y > 0){
31507                     scale = targetWidth / width;
31508                     
31509                     if(width < height){
31510                         scale = targetHeight / height;
31511                     }
31512                 }
31513                 
31514                 context.scale(scale, scale);
31515                 
31516                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31517                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31518
31519                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31520                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31521                 
31522                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31523                 
31524                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31525                 
31526                 break;
31527             case 180 :
31528                 
31529                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31530                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31531                 
31532                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31533                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31534                 
31535                 var targetWidth = this.minWidth - 2 * x;
31536                 var targetHeight = this.minHeight - 2 * y;
31537                 
31538                 var scale = 1;
31539                 
31540                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31541                     scale = targetWidth / width;
31542                 }
31543                 
31544                 if(x > 0 && y == 0){
31545                     scale = targetHeight / height;
31546                 }
31547                 
31548                 if(x > 0 && y > 0){
31549                     scale = targetWidth / width;
31550                     
31551                     if(width < height){
31552                         scale = targetHeight / height;
31553                     }
31554                 }
31555                 
31556                 context.scale(scale, scale);
31557                 
31558                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31559                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31560
31561                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31562                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31563
31564                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31565                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31566                 
31567                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31568                 
31569                 break;
31570             case 270 :
31571                 
31572                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31573                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31574                 
31575                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31576                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31577                 
31578                 var targetWidth = this.minWidth - 2 * x;
31579                 var targetHeight = this.minHeight - 2 * y;
31580                 
31581                 var scale = 1;
31582                 
31583                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31584                     scale = targetWidth / width;
31585                 }
31586                 
31587                 if(x > 0 && y == 0){
31588                     scale = targetHeight / height;
31589                 }
31590                 
31591                 if(x > 0 && y > 0){
31592                     scale = targetWidth / width;
31593                     
31594                     if(width < height){
31595                         scale = targetHeight / height;
31596                     }
31597                 }
31598                 
31599                 context.scale(scale, scale);
31600                 
31601                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31602                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31603
31604                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31605                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31606                 
31607                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31608                 
31609                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31610                 
31611                 break;
31612             default : 
31613                 break;
31614         }
31615         
31616         this.cropData = canvas.toDataURL(this.cropType);
31617         
31618         if(this.fireEvent('crop', this, this.cropData) !== false){
31619             this.process(this.file, this.cropData);
31620         }
31621         
31622         return;
31623         
31624     },
31625     
31626     setThumbBoxSize : function()
31627     {
31628         var width, height;
31629         
31630         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31631             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31632             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31633             
31634             this.minWidth = width;
31635             this.minHeight = height;
31636             
31637             if(this.rotate == 90 || this.rotate == 270){
31638                 this.minWidth = height;
31639                 this.minHeight = width;
31640             }
31641         }
31642         
31643         height = 300;
31644         width = Math.ceil(this.minWidth * height / this.minHeight);
31645         
31646         if(this.minWidth > this.minHeight){
31647             width = 300;
31648             height = Math.ceil(this.minHeight * width / this.minWidth);
31649         }
31650         
31651         this.thumbEl.setStyle({
31652             width : width + 'px',
31653             height : height + 'px'
31654         });
31655
31656         return;
31657             
31658     },
31659     
31660     setThumbBoxPosition : function()
31661     {
31662         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31663         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31664         
31665         this.thumbEl.setLeft(x);
31666         this.thumbEl.setTop(y);
31667         
31668     },
31669     
31670     baseRotateLevel : function()
31671     {
31672         this.baseRotate = 1;
31673         
31674         if(
31675                 typeof(this.exif) != 'undefined' &&
31676                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31677                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31678         ){
31679             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31680         }
31681         
31682         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31683         
31684     },
31685     
31686     baseScaleLevel : function()
31687     {
31688         var width, height;
31689         
31690         if(this.isDocument){
31691             
31692             if(this.baseRotate == 6 || this.baseRotate == 8){
31693             
31694                 height = this.thumbEl.getHeight();
31695                 this.baseScale = height / this.imageEl.OriginWidth;
31696
31697                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31698                     width = this.thumbEl.getWidth();
31699                     this.baseScale = width / this.imageEl.OriginHeight;
31700                 }
31701
31702                 return;
31703             }
31704
31705             height = this.thumbEl.getHeight();
31706             this.baseScale = height / this.imageEl.OriginHeight;
31707
31708             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31709                 width = this.thumbEl.getWidth();
31710                 this.baseScale = width / this.imageEl.OriginWidth;
31711             }
31712
31713             return;
31714         }
31715         
31716         if(this.baseRotate == 6 || this.baseRotate == 8){
31717             
31718             width = this.thumbEl.getHeight();
31719             this.baseScale = width / this.imageEl.OriginHeight;
31720             
31721             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31722                 height = this.thumbEl.getWidth();
31723                 this.baseScale = height / this.imageEl.OriginHeight;
31724             }
31725             
31726             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31727                 height = this.thumbEl.getWidth();
31728                 this.baseScale = height / this.imageEl.OriginHeight;
31729                 
31730                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31731                     width = this.thumbEl.getHeight();
31732                     this.baseScale = width / this.imageEl.OriginWidth;
31733                 }
31734             }
31735             
31736             return;
31737         }
31738         
31739         width = this.thumbEl.getWidth();
31740         this.baseScale = width / this.imageEl.OriginWidth;
31741         
31742         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31743             height = this.thumbEl.getHeight();
31744             this.baseScale = height / this.imageEl.OriginHeight;
31745         }
31746         
31747         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31748             
31749             height = this.thumbEl.getHeight();
31750             this.baseScale = height / this.imageEl.OriginHeight;
31751             
31752             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31753                 width = this.thumbEl.getWidth();
31754                 this.baseScale = width / this.imageEl.OriginWidth;
31755             }
31756             
31757         }
31758         
31759         return;
31760     },
31761     
31762     getScaleLevel : function()
31763     {
31764         return this.baseScale * Math.pow(1.1, this.scale);
31765     },
31766     
31767     onTouchStart : function(e)
31768     {
31769         if(!this.canvasLoaded){
31770             this.beforeSelectFile(e);
31771             return;
31772         }
31773         
31774         var touches = e.browserEvent.touches;
31775         
31776         if(!touches){
31777             return;
31778         }
31779         
31780         if(touches.length == 1){
31781             this.onMouseDown(e);
31782             return;
31783         }
31784         
31785         if(touches.length != 2){
31786             return;
31787         }
31788         
31789         var coords = [];
31790         
31791         for(var i = 0, finger; finger = touches[i]; i++){
31792             coords.push(finger.pageX, finger.pageY);
31793         }
31794         
31795         var x = Math.pow(coords[0] - coords[2], 2);
31796         var y = Math.pow(coords[1] - coords[3], 2);
31797         
31798         this.startDistance = Math.sqrt(x + y);
31799         
31800         this.startScale = this.scale;
31801         
31802         this.pinching = true;
31803         this.dragable = false;
31804         
31805     },
31806     
31807     onTouchMove : function(e)
31808     {
31809         if(!this.pinching && !this.dragable){
31810             return;
31811         }
31812         
31813         var touches = e.browserEvent.touches;
31814         
31815         if(!touches){
31816             return;
31817         }
31818         
31819         if(this.dragable){
31820             this.onMouseMove(e);
31821             return;
31822         }
31823         
31824         var coords = [];
31825         
31826         for(var i = 0, finger; finger = touches[i]; i++){
31827             coords.push(finger.pageX, finger.pageY);
31828         }
31829         
31830         var x = Math.pow(coords[0] - coords[2], 2);
31831         var y = Math.pow(coords[1] - coords[3], 2);
31832         
31833         this.endDistance = Math.sqrt(x + y);
31834         
31835         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31836         
31837         if(!this.zoomable()){
31838             this.scale = this.startScale;
31839             return;
31840         }
31841         
31842         this.draw();
31843         
31844     },
31845     
31846     onTouchEnd : function(e)
31847     {
31848         this.pinching = false;
31849         this.dragable = false;
31850         
31851     },
31852     
31853     process : function(file, crop)
31854     {
31855         if(this.loadMask){
31856             this.maskEl.mask(this.loadingText);
31857         }
31858         
31859         this.xhr = new XMLHttpRequest();
31860         
31861         file.xhr = this.xhr;
31862
31863         this.xhr.open(this.method, this.url, true);
31864         
31865         var headers = {
31866             "Accept": "application/json",
31867             "Cache-Control": "no-cache",
31868             "X-Requested-With": "XMLHttpRequest"
31869         };
31870         
31871         for (var headerName in headers) {
31872             var headerValue = headers[headerName];
31873             if (headerValue) {
31874                 this.xhr.setRequestHeader(headerName, headerValue);
31875             }
31876         }
31877         
31878         var _this = this;
31879         
31880         this.xhr.onload = function()
31881         {
31882             _this.xhrOnLoad(_this.xhr);
31883         }
31884         
31885         this.xhr.onerror = function()
31886         {
31887             _this.xhrOnError(_this.xhr);
31888         }
31889         
31890         var formData = new FormData();
31891
31892         formData.append('returnHTML', 'NO');
31893         
31894         if(crop){
31895             formData.append('crop', crop);
31896         }
31897         
31898         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31899             formData.append(this.paramName, file, file.name);
31900         }
31901         
31902         if(typeof(file.filename) != 'undefined'){
31903             formData.append('filename', file.filename);
31904         }
31905         
31906         if(typeof(file.mimetype) != 'undefined'){
31907             formData.append('mimetype', file.mimetype);
31908         }
31909         
31910         if(this.fireEvent('arrange', this, formData) != false){
31911             this.xhr.send(formData);
31912         };
31913     },
31914     
31915     xhrOnLoad : function(xhr)
31916     {
31917         if(this.loadMask){
31918             this.maskEl.unmask();
31919         }
31920         
31921         if (xhr.readyState !== 4) {
31922             this.fireEvent('exception', this, xhr);
31923             return;
31924         }
31925
31926         var response = Roo.decode(xhr.responseText);
31927         
31928         if(!response.success){
31929             this.fireEvent('exception', this, xhr);
31930             return;
31931         }
31932         
31933         var response = Roo.decode(xhr.responseText);
31934         
31935         this.fireEvent('upload', this, response);
31936         
31937     },
31938     
31939     xhrOnError : function()
31940     {
31941         if(this.loadMask){
31942             this.maskEl.unmask();
31943         }
31944         
31945         Roo.log('xhr on error');
31946         
31947         var response = Roo.decode(xhr.responseText);
31948           
31949         Roo.log(response);
31950         
31951     },
31952     
31953     prepare : function(file)
31954     {   
31955         if(this.loadMask){
31956             this.maskEl.mask(this.loadingText);
31957         }
31958         
31959         this.file = false;
31960         this.exif = {};
31961         
31962         if(typeof(file) === 'string'){
31963             this.loadCanvas(file);
31964             return;
31965         }
31966         
31967         if(!file || !this.urlAPI){
31968             return;
31969         }
31970         
31971         this.file = file;
31972         this.cropType = file.type;
31973         
31974         var _this = this;
31975         
31976         if(this.fireEvent('prepare', this, this.file) != false){
31977             
31978             var reader = new FileReader();
31979             
31980             reader.onload = function (e) {
31981                 if (e.target.error) {
31982                     Roo.log(e.target.error);
31983                     return;
31984                 }
31985                 
31986                 var buffer = e.target.result,
31987                     dataView = new DataView(buffer),
31988                     offset = 2,
31989                     maxOffset = dataView.byteLength - 4,
31990                     markerBytes,
31991                     markerLength;
31992                 
31993                 if (dataView.getUint16(0) === 0xffd8) {
31994                     while (offset < maxOffset) {
31995                         markerBytes = dataView.getUint16(offset);
31996                         
31997                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31998                             markerLength = dataView.getUint16(offset + 2) + 2;
31999                             if (offset + markerLength > dataView.byteLength) {
32000                                 Roo.log('Invalid meta data: Invalid segment size.');
32001                                 break;
32002                             }
32003                             
32004                             if(markerBytes == 0xffe1){
32005                                 _this.parseExifData(
32006                                     dataView,
32007                                     offset,
32008                                     markerLength
32009                                 );
32010                             }
32011                             
32012                             offset += markerLength;
32013                             
32014                             continue;
32015                         }
32016                         
32017                         break;
32018                     }
32019                     
32020                 }
32021                 
32022                 var url = _this.urlAPI.createObjectURL(_this.file);
32023                 
32024                 _this.loadCanvas(url);
32025                 
32026                 return;
32027             }
32028             
32029             reader.readAsArrayBuffer(this.file);
32030             
32031         }
32032         
32033     },
32034     
32035     parseExifData : function(dataView, offset, length)
32036     {
32037         var tiffOffset = offset + 10,
32038             littleEndian,
32039             dirOffset;
32040     
32041         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32042             // No Exif data, might be XMP data instead
32043             return;
32044         }
32045         
32046         // Check for the ASCII code for "Exif" (0x45786966):
32047         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32048             // No Exif data, might be XMP data instead
32049             return;
32050         }
32051         if (tiffOffset + 8 > dataView.byteLength) {
32052             Roo.log('Invalid Exif data: Invalid segment size.');
32053             return;
32054         }
32055         // Check for the two null bytes:
32056         if (dataView.getUint16(offset + 8) !== 0x0000) {
32057             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32058             return;
32059         }
32060         // Check the byte alignment:
32061         switch (dataView.getUint16(tiffOffset)) {
32062         case 0x4949:
32063             littleEndian = true;
32064             break;
32065         case 0x4D4D:
32066             littleEndian = false;
32067             break;
32068         default:
32069             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32070             return;
32071         }
32072         // Check for the TIFF tag marker (0x002A):
32073         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32074             Roo.log('Invalid Exif data: Missing TIFF marker.');
32075             return;
32076         }
32077         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32078         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32079         
32080         this.parseExifTags(
32081             dataView,
32082             tiffOffset,
32083             tiffOffset + dirOffset,
32084             littleEndian
32085         );
32086     },
32087     
32088     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32089     {
32090         var tagsNumber,
32091             dirEndOffset,
32092             i;
32093         if (dirOffset + 6 > dataView.byteLength) {
32094             Roo.log('Invalid Exif data: Invalid directory offset.');
32095             return;
32096         }
32097         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32098         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32099         if (dirEndOffset + 4 > dataView.byteLength) {
32100             Roo.log('Invalid Exif data: Invalid directory size.');
32101             return;
32102         }
32103         for (i = 0; i < tagsNumber; i += 1) {
32104             this.parseExifTag(
32105                 dataView,
32106                 tiffOffset,
32107                 dirOffset + 2 + 12 * i, // tag offset
32108                 littleEndian
32109             );
32110         }
32111         // Return the offset to the next directory:
32112         return dataView.getUint32(dirEndOffset, littleEndian);
32113     },
32114     
32115     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32116     {
32117         var tag = dataView.getUint16(offset, littleEndian);
32118         
32119         this.exif[tag] = this.getExifValue(
32120             dataView,
32121             tiffOffset,
32122             offset,
32123             dataView.getUint16(offset + 2, littleEndian), // tag type
32124             dataView.getUint32(offset + 4, littleEndian), // tag length
32125             littleEndian
32126         );
32127     },
32128     
32129     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32130     {
32131         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32132             tagSize,
32133             dataOffset,
32134             values,
32135             i,
32136             str,
32137             c;
32138     
32139         if (!tagType) {
32140             Roo.log('Invalid Exif data: Invalid tag type.');
32141             return;
32142         }
32143         
32144         tagSize = tagType.size * length;
32145         // Determine if the value is contained in the dataOffset bytes,
32146         // or if the value at the dataOffset is a pointer to the actual data:
32147         dataOffset = tagSize > 4 ?
32148                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32149         if (dataOffset + tagSize > dataView.byteLength) {
32150             Roo.log('Invalid Exif data: Invalid data offset.');
32151             return;
32152         }
32153         if (length === 1) {
32154             return tagType.getValue(dataView, dataOffset, littleEndian);
32155         }
32156         values = [];
32157         for (i = 0; i < length; i += 1) {
32158             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32159         }
32160         
32161         if (tagType.ascii) {
32162             str = '';
32163             // Concatenate the chars:
32164             for (i = 0; i < values.length; i += 1) {
32165                 c = values[i];
32166                 // Ignore the terminating NULL byte(s):
32167                 if (c === '\u0000') {
32168                     break;
32169                 }
32170                 str += c;
32171             }
32172             return str;
32173         }
32174         return values;
32175     }
32176     
32177 });
32178
32179 Roo.apply(Roo.bootstrap.UploadCropbox, {
32180     tags : {
32181         'Orientation': 0x0112
32182     },
32183     
32184     Orientation: {
32185             1: 0, //'top-left',
32186 //            2: 'top-right',
32187             3: 180, //'bottom-right',
32188 //            4: 'bottom-left',
32189 //            5: 'left-top',
32190             6: 90, //'right-top',
32191 //            7: 'right-bottom',
32192             8: 270 //'left-bottom'
32193     },
32194     
32195     exifTagTypes : {
32196         // byte, 8-bit unsigned int:
32197         1: {
32198             getValue: function (dataView, dataOffset) {
32199                 return dataView.getUint8(dataOffset);
32200             },
32201             size: 1
32202         },
32203         // ascii, 8-bit byte:
32204         2: {
32205             getValue: function (dataView, dataOffset) {
32206                 return String.fromCharCode(dataView.getUint8(dataOffset));
32207             },
32208             size: 1,
32209             ascii: true
32210         },
32211         // short, 16 bit int:
32212         3: {
32213             getValue: function (dataView, dataOffset, littleEndian) {
32214                 return dataView.getUint16(dataOffset, littleEndian);
32215             },
32216             size: 2
32217         },
32218         // long, 32 bit int:
32219         4: {
32220             getValue: function (dataView, dataOffset, littleEndian) {
32221                 return dataView.getUint32(dataOffset, littleEndian);
32222             },
32223             size: 4
32224         },
32225         // rational = two long values, first is numerator, second is denominator:
32226         5: {
32227             getValue: function (dataView, dataOffset, littleEndian) {
32228                 return dataView.getUint32(dataOffset, littleEndian) /
32229                     dataView.getUint32(dataOffset + 4, littleEndian);
32230             },
32231             size: 8
32232         },
32233         // slong, 32 bit signed int:
32234         9: {
32235             getValue: function (dataView, dataOffset, littleEndian) {
32236                 return dataView.getInt32(dataOffset, littleEndian);
32237             },
32238             size: 4
32239         },
32240         // srational, two slongs, first is numerator, second is denominator:
32241         10: {
32242             getValue: function (dataView, dataOffset, littleEndian) {
32243                 return dataView.getInt32(dataOffset, littleEndian) /
32244                     dataView.getInt32(dataOffset + 4, littleEndian);
32245             },
32246             size: 8
32247         }
32248     },
32249     
32250     footer : {
32251         STANDARD : [
32252             {
32253                 tag : 'div',
32254                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32255                 action : 'rotate-left',
32256                 cn : [
32257                     {
32258                         tag : 'button',
32259                         cls : 'btn btn-default',
32260                         html : '<i class="fa fa-undo"></i>'
32261                     }
32262                 ]
32263             },
32264             {
32265                 tag : 'div',
32266                 cls : 'btn-group roo-upload-cropbox-picture',
32267                 action : 'picture',
32268                 cn : [
32269                     {
32270                         tag : 'button',
32271                         cls : 'btn btn-default',
32272                         html : '<i class="fa fa-picture-o"></i>'
32273                     }
32274                 ]
32275             },
32276             {
32277                 tag : 'div',
32278                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32279                 action : 'rotate-right',
32280                 cn : [
32281                     {
32282                         tag : 'button',
32283                         cls : 'btn btn-default',
32284                         html : '<i class="fa fa-repeat"></i>'
32285                     }
32286                 ]
32287             }
32288         ],
32289         DOCUMENT : [
32290             {
32291                 tag : 'div',
32292                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32293                 action : 'rotate-left',
32294                 cn : [
32295                     {
32296                         tag : 'button',
32297                         cls : 'btn btn-default',
32298                         html : '<i class="fa fa-undo"></i>'
32299                     }
32300                 ]
32301             },
32302             {
32303                 tag : 'div',
32304                 cls : 'btn-group roo-upload-cropbox-download',
32305                 action : 'download',
32306                 cn : [
32307                     {
32308                         tag : 'button',
32309                         cls : 'btn btn-default',
32310                         html : '<i class="fa fa-download"></i>'
32311                     }
32312                 ]
32313             },
32314             {
32315                 tag : 'div',
32316                 cls : 'btn-group roo-upload-cropbox-crop',
32317                 action : 'crop',
32318                 cn : [
32319                     {
32320                         tag : 'button',
32321                         cls : 'btn btn-default',
32322                         html : '<i class="fa fa-crop"></i>'
32323                     }
32324                 ]
32325             },
32326             {
32327                 tag : 'div',
32328                 cls : 'btn-group roo-upload-cropbox-trash',
32329                 action : 'trash',
32330                 cn : [
32331                     {
32332                         tag : 'button',
32333                         cls : 'btn btn-default',
32334                         html : '<i class="fa fa-trash"></i>'
32335                     }
32336                 ]
32337             },
32338             {
32339                 tag : 'div',
32340                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32341                 action : 'rotate-right',
32342                 cn : [
32343                     {
32344                         tag : 'button',
32345                         cls : 'btn btn-default',
32346                         html : '<i class="fa fa-repeat"></i>'
32347                     }
32348                 ]
32349             }
32350         ],
32351         ROTATOR : [
32352             {
32353                 tag : 'div',
32354                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32355                 action : 'rotate-left',
32356                 cn : [
32357                     {
32358                         tag : 'button',
32359                         cls : 'btn btn-default',
32360                         html : '<i class="fa fa-undo"></i>'
32361                     }
32362                 ]
32363             },
32364             {
32365                 tag : 'div',
32366                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32367                 action : 'rotate-right',
32368                 cn : [
32369                     {
32370                         tag : 'button',
32371                         cls : 'btn btn-default',
32372                         html : '<i class="fa fa-repeat"></i>'
32373                     }
32374                 ]
32375             }
32376         ]
32377     }
32378 });
32379
32380 /*
32381 * Licence: LGPL
32382 */
32383
32384 /**
32385  * @class Roo.bootstrap.DocumentManager
32386  * @extends Roo.bootstrap.Component
32387  * Bootstrap DocumentManager class
32388  * @cfg {String} paramName default 'imageUpload'
32389  * @cfg {String} toolTipName default 'filename'
32390  * @cfg {String} method default POST
32391  * @cfg {String} url action url
32392  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32393  * @cfg {Boolean} multiple multiple upload default true
32394  * @cfg {Number} thumbSize default 300
32395  * @cfg {String} fieldLabel
32396  * @cfg {Number} labelWidth default 4
32397  * @cfg {String} labelAlign (left|top) default left
32398  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32399 * @cfg {Number} labellg set the width of label (1-12)
32400  * @cfg {Number} labelmd set the width of label (1-12)
32401  * @cfg {Number} labelsm set the width of label (1-12)
32402  * @cfg {Number} labelxs set the width of label (1-12)
32403  * 
32404  * @constructor
32405  * Create a new DocumentManager
32406  * @param {Object} config The config object
32407  */
32408
32409 Roo.bootstrap.DocumentManager = function(config){
32410     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32411     
32412     this.files = [];
32413     this.delegates = [];
32414     
32415     this.addEvents({
32416         /**
32417          * @event initial
32418          * Fire when initial the DocumentManager
32419          * @param {Roo.bootstrap.DocumentManager} this
32420          */
32421         "initial" : true,
32422         /**
32423          * @event inspect
32424          * inspect selected file
32425          * @param {Roo.bootstrap.DocumentManager} this
32426          * @param {File} file
32427          */
32428         "inspect" : true,
32429         /**
32430          * @event exception
32431          * Fire when xhr load exception
32432          * @param {Roo.bootstrap.DocumentManager} this
32433          * @param {XMLHttpRequest} xhr
32434          */
32435         "exception" : true,
32436         /**
32437          * @event afterupload
32438          * Fire when xhr load exception
32439          * @param {Roo.bootstrap.DocumentManager} this
32440          * @param {XMLHttpRequest} xhr
32441          */
32442         "afterupload" : true,
32443         /**
32444          * @event prepare
32445          * prepare the form data
32446          * @param {Roo.bootstrap.DocumentManager} this
32447          * @param {Object} formData
32448          */
32449         "prepare" : true,
32450         /**
32451          * @event remove
32452          * Fire when remove the file
32453          * @param {Roo.bootstrap.DocumentManager} this
32454          * @param {Object} file
32455          */
32456         "remove" : true,
32457         /**
32458          * @event refresh
32459          * Fire after refresh the file
32460          * @param {Roo.bootstrap.DocumentManager} this
32461          */
32462         "refresh" : true,
32463         /**
32464          * @event click
32465          * Fire after click the image
32466          * @param {Roo.bootstrap.DocumentManager} this
32467          * @param {Object} file
32468          */
32469         "click" : true,
32470         /**
32471          * @event edit
32472          * Fire when upload a image and editable set to true
32473          * @param {Roo.bootstrap.DocumentManager} this
32474          * @param {Object} file
32475          */
32476         "edit" : true,
32477         /**
32478          * @event beforeselectfile
32479          * Fire before select file
32480          * @param {Roo.bootstrap.DocumentManager} this
32481          */
32482         "beforeselectfile" : true,
32483         /**
32484          * @event process
32485          * Fire before process file
32486          * @param {Roo.bootstrap.DocumentManager} this
32487          * @param {Object} file
32488          */
32489         "process" : true,
32490         /**
32491          * @event previewrendered
32492          * Fire when preview rendered
32493          * @param {Roo.bootstrap.DocumentManager} this
32494          * @param {Object} file
32495          */
32496         "previewrendered" : true,
32497         /**
32498          */
32499         "previewResize" : true
32500         
32501     });
32502 };
32503
32504 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32505     
32506     boxes : 0,
32507     inputName : '',
32508     thumbSize : 300,
32509     multiple : true,
32510     files : false,
32511     method : 'POST',
32512     url : '',
32513     paramName : 'imageUpload',
32514     toolTipName : 'filename',
32515     fieldLabel : '',
32516     labelWidth : 4,
32517     labelAlign : 'left',
32518     editable : true,
32519     delegates : false,
32520     xhr : false, 
32521     
32522     labellg : 0,
32523     labelmd : 0,
32524     labelsm : 0,
32525     labelxs : 0,
32526     
32527     getAutoCreate : function()
32528     {   
32529         var managerWidget = {
32530             tag : 'div',
32531             cls : 'roo-document-manager',
32532             cn : [
32533                 {
32534                     tag : 'input',
32535                     cls : 'roo-document-manager-selector',
32536                     type : 'file'
32537                 },
32538                 {
32539                     tag : 'div',
32540                     cls : 'roo-document-manager-uploader',
32541                     cn : [
32542                         {
32543                             tag : 'div',
32544                             cls : 'roo-document-manager-upload-btn',
32545                             html : '<i class="fa fa-plus"></i>'
32546                         }
32547                     ]
32548                     
32549                 }
32550             ]
32551         };
32552         
32553         var content = [
32554             {
32555                 tag : 'div',
32556                 cls : 'column col-md-12',
32557                 cn : managerWidget
32558             }
32559         ];
32560         
32561         if(this.fieldLabel.length){
32562             
32563             content = [
32564                 {
32565                     tag : 'div',
32566                     cls : 'column col-md-12',
32567                     html : this.fieldLabel
32568                 },
32569                 {
32570                     tag : 'div',
32571                     cls : 'column col-md-12',
32572                     cn : managerWidget
32573                 }
32574             ];
32575
32576             if(this.labelAlign == 'left'){
32577                 content = [
32578                     {
32579                         tag : 'div',
32580                         cls : 'column',
32581                         html : this.fieldLabel
32582                     },
32583                     {
32584                         tag : 'div',
32585                         cls : 'column',
32586                         cn : managerWidget
32587                     }
32588                 ];
32589                 
32590                 if(this.labelWidth > 12){
32591                     content[0].style = "width: " + this.labelWidth + 'px';
32592                 }
32593
32594                 if(this.labelWidth < 13 && this.labelmd == 0){
32595                     this.labelmd = this.labelWidth;
32596                 }
32597
32598                 if(this.labellg > 0){
32599                     content[0].cls += ' col-lg-' + this.labellg;
32600                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32601                 }
32602
32603                 if(this.labelmd > 0){
32604                     content[0].cls += ' col-md-' + this.labelmd;
32605                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32606                 }
32607
32608                 if(this.labelsm > 0){
32609                     content[0].cls += ' col-sm-' + this.labelsm;
32610                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32611                 }
32612
32613                 if(this.labelxs > 0){
32614                     content[0].cls += ' col-xs-' + this.labelxs;
32615                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32616                 }
32617                 
32618             }
32619         }
32620         
32621         var cfg = {
32622             tag : 'div',
32623             cls : 'row clearfix',
32624             cn : content
32625         };
32626         
32627         return cfg;
32628         
32629     },
32630     
32631     initEvents : function()
32632     {
32633         this.managerEl = this.el.select('.roo-document-manager', true).first();
32634         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32635         
32636         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32637         this.selectorEl.hide();
32638         
32639         if(this.multiple){
32640             this.selectorEl.attr('multiple', 'multiple');
32641         }
32642         
32643         this.selectorEl.on('change', this.onFileSelected, this);
32644         
32645         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32646         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32647         
32648         this.uploader.on('click', this.onUploaderClick, this);
32649         
32650         this.renderProgressDialog();
32651         
32652         var _this = this;
32653         
32654         window.addEventListener("resize", function() { _this.refresh(); } );
32655         
32656         this.fireEvent('initial', this);
32657     },
32658     
32659     renderProgressDialog : function()
32660     {
32661         var _this = this;
32662         
32663         this.progressDialog = new Roo.bootstrap.Modal({
32664             cls : 'roo-document-manager-progress-dialog',
32665             allow_close : false,
32666             animate : false,
32667             title : '',
32668             buttons : [
32669                 {
32670                     name  :'cancel',
32671                     weight : 'danger',
32672                     html : 'Cancel'
32673                 }
32674             ], 
32675             listeners : { 
32676                 btnclick : function() {
32677                     _this.uploadCancel();
32678                     this.hide();
32679                 }
32680             }
32681         });
32682          
32683         this.progressDialog.render(Roo.get(document.body));
32684          
32685         this.progress = new Roo.bootstrap.Progress({
32686             cls : 'roo-document-manager-progress',
32687             active : true,
32688             striped : true
32689         });
32690         
32691         this.progress.render(this.progressDialog.getChildContainer());
32692         
32693         this.progressBar = new Roo.bootstrap.ProgressBar({
32694             cls : 'roo-document-manager-progress-bar',
32695             aria_valuenow : 0,
32696             aria_valuemin : 0,
32697             aria_valuemax : 12,
32698             panel : 'success'
32699         });
32700         
32701         this.progressBar.render(this.progress.getChildContainer());
32702     },
32703     
32704     onUploaderClick : function(e)
32705     {
32706         e.preventDefault();
32707      
32708         if(this.fireEvent('beforeselectfile', this) != false){
32709             this.selectorEl.dom.click();
32710         }
32711         
32712     },
32713     
32714     onFileSelected : function(e)
32715     {
32716         e.preventDefault();
32717         
32718         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32719             return;
32720         }
32721         
32722         Roo.each(this.selectorEl.dom.files, function(file){
32723             if(this.fireEvent('inspect', this, file) != false){
32724                 this.files.push(file);
32725             }
32726         }, this);
32727         
32728         this.queue();
32729         
32730     },
32731     
32732     queue : function()
32733     {
32734         this.selectorEl.dom.value = '';
32735         
32736         if(!this.files || !this.files.length){
32737             return;
32738         }
32739         
32740         if(this.boxes > 0 && this.files.length > this.boxes){
32741             this.files = this.files.slice(0, this.boxes);
32742         }
32743         
32744         this.uploader.show();
32745         
32746         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32747             this.uploader.hide();
32748         }
32749         
32750         var _this = this;
32751         
32752         var files = [];
32753         
32754         var docs = [];
32755         
32756         Roo.each(this.files, function(file){
32757             
32758             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32759                 var f = this.renderPreview(file);
32760                 files.push(f);
32761                 return;
32762             }
32763             
32764             if(file.type.indexOf('image') != -1){
32765                 this.delegates.push(
32766                     (function(){
32767                         _this.process(file);
32768                     }).createDelegate(this)
32769                 );
32770         
32771                 return;
32772             }
32773             
32774             docs.push(
32775                 (function(){
32776                     _this.process(file);
32777                 }).createDelegate(this)
32778             );
32779             
32780         }, this);
32781         
32782         this.files = files;
32783         
32784         this.delegates = this.delegates.concat(docs);
32785         
32786         if(!this.delegates.length){
32787             this.refresh();
32788             return;
32789         }
32790         
32791         this.progressBar.aria_valuemax = this.delegates.length;
32792         
32793         this.arrange();
32794         
32795         return;
32796     },
32797     
32798     arrange : function()
32799     {
32800         if(!this.delegates.length){
32801             this.progressDialog.hide();
32802             this.refresh();
32803             return;
32804         }
32805         
32806         var delegate = this.delegates.shift();
32807         
32808         this.progressDialog.show();
32809         
32810         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32811         
32812         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32813         
32814         delegate();
32815     },
32816     
32817     refresh : function()
32818     {
32819         this.uploader.show();
32820         
32821         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32822             this.uploader.hide();
32823         }
32824         
32825         Roo.isTouch ? this.closable(false) : this.closable(true);
32826         
32827         this.fireEvent('refresh', this);
32828     },
32829     
32830     onRemove : function(e, el, o)
32831     {
32832         e.preventDefault();
32833         
32834         this.fireEvent('remove', this, o);
32835         
32836     },
32837     
32838     remove : function(o)
32839     {
32840         var files = [];
32841         
32842         Roo.each(this.files, function(file){
32843             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32844                 files.push(file);
32845                 return;
32846             }
32847
32848             o.target.remove();
32849
32850         }, this);
32851         
32852         this.files = files;
32853         
32854         this.refresh();
32855     },
32856     
32857     clear : function()
32858     {
32859         Roo.each(this.files, function(file){
32860             if(!file.target){
32861                 return;
32862             }
32863             
32864             file.target.remove();
32865
32866         }, this);
32867         
32868         this.files = [];
32869         
32870         this.refresh();
32871     },
32872     
32873     onClick : function(e, el, o)
32874     {
32875         e.preventDefault();
32876         
32877         this.fireEvent('click', this, o);
32878         
32879     },
32880     
32881     closable : function(closable)
32882     {
32883         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32884             
32885             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32886             
32887             if(closable){
32888                 el.show();
32889                 return;
32890             }
32891             
32892             el.hide();
32893             
32894         }, this);
32895     },
32896     
32897     xhrOnLoad : function(xhr)
32898     {
32899         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32900             el.remove();
32901         }, this);
32902         
32903         if (xhr.readyState !== 4) {
32904             this.arrange();
32905             this.fireEvent('exception', this, xhr);
32906             return;
32907         }
32908
32909         var response = Roo.decode(xhr.responseText);
32910         
32911         if(!response.success){
32912             this.arrange();
32913             this.fireEvent('exception', this, xhr);
32914             return;
32915         }
32916         
32917         var file = this.renderPreview(response.data);
32918         
32919         this.files.push(file);
32920         
32921         this.arrange();
32922         
32923         this.fireEvent('afterupload', this, xhr);
32924         
32925     },
32926     
32927     xhrOnError : function(xhr)
32928     {
32929         Roo.log('xhr on error');
32930         
32931         var response = Roo.decode(xhr.responseText);
32932           
32933         Roo.log(response);
32934         
32935         this.arrange();
32936     },
32937     
32938     process : function(file)
32939     {
32940         if(this.fireEvent('process', this, file) !== false){
32941             if(this.editable && file.type.indexOf('image') != -1){
32942                 this.fireEvent('edit', this, file);
32943                 return;
32944             }
32945
32946             this.uploadStart(file, false);
32947
32948             return;
32949         }
32950         
32951     },
32952     
32953     uploadStart : function(file, crop)
32954     {
32955         this.xhr = new XMLHttpRequest();
32956         
32957         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32958             this.arrange();
32959             return;
32960         }
32961         
32962         file.xhr = this.xhr;
32963             
32964         this.managerEl.createChild({
32965             tag : 'div',
32966             cls : 'roo-document-manager-loading',
32967             cn : [
32968                 {
32969                     tag : 'div',
32970                     tooltip : file.name,
32971                     cls : 'roo-document-manager-thumb',
32972                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32973                 }
32974             ]
32975
32976         });
32977
32978         this.xhr.open(this.method, this.url, true);
32979         
32980         var headers = {
32981             "Accept": "application/json",
32982             "Cache-Control": "no-cache",
32983             "X-Requested-With": "XMLHttpRequest"
32984         };
32985         
32986         for (var headerName in headers) {
32987             var headerValue = headers[headerName];
32988             if (headerValue) {
32989                 this.xhr.setRequestHeader(headerName, headerValue);
32990             }
32991         }
32992         
32993         var _this = this;
32994         
32995         this.xhr.onload = function()
32996         {
32997             _this.xhrOnLoad(_this.xhr);
32998         }
32999         
33000         this.xhr.onerror = function()
33001         {
33002             _this.xhrOnError(_this.xhr);
33003         }
33004         
33005         var formData = new FormData();
33006
33007         formData.append('returnHTML', 'NO');
33008         
33009         if(crop){
33010             formData.append('crop', crop);
33011         }
33012         
33013         formData.append(this.paramName, file, file.name);
33014         
33015         var options = {
33016             file : file, 
33017             manually : false
33018         };
33019         
33020         if(this.fireEvent('prepare', this, formData, options) != false){
33021             
33022             if(options.manually){
33023                 return;
33024             }
33025             
33026             this.xhr.send(formData);
33027             return;
33028         };
33029         
33030         this.uploadCancel();
33031     },
33032     
33033     uploadCancel : function()
33034     {
33035         if (this.xhr) {
33036             this.xhr.abort();
33037         }
33038         
33039         this.delegates = [];
33040         
33041         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33042             el.remove();
33043         }, this);
33044         
33045         this.arrange();
33046     },
33047     
33048     renderPreview : function(file)
33049     {
33050         if(typeof(file.target) != 'undefined' && file.target){
33051             return file;
33052         }
33053         
33054         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33055         
33056         var previewEl = this.managerEl.createChild({
33057             tag : 'div',
33058             cls : 'roo-document-manager-preview',
33059             cn : [
33060                 {
33061                     tag : 'div',
33062                     tooltip : file[this.toolTipName],
33063                     cls : 'roo-document-manager-thumb',
33064                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33065                 },
33066                 {
33067                     tag : 'button',
33068                     cls : 'close',
33069                     html : '<i class="fa fa-times-circle"></i>'
33070                 }
33071             ]
33072         });
33073
33074         var close = previewEl.select('button.close', true).first();
33075
33076         close.on('click', this.onRemove, this, file);
33077
33078         file.target = previewEl;
33079
33080         var image = previewEl.select('img', true).first();
33081         
33082         var _this = this;
33083         
33084         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33085         
33086         image.on('click', this.onClick, this, file);
33087         
33088         this.fireEvent('previewrendered', this, file);
33089         
33090         return file;
33091         
33092     },
33093     
33094     onPreviewLoad : function(file, image)
33095     {
33096         if(typeof(file.target) == 'undefined' || !file.target){
33097             return;
33098         }
33099         
33100         var width = image.dom.naturalWidth || image.dom.width;
33101         var height = image.dom.naturalHeight || image.dom.height;
33102         
33103         if(!this.previewResize) {
33104             return;
33105         }
33106         
33107         if(width > height){
33108             file.target.addClass('wide');
33109             return;
33110         }
33111         
33112         file.target.addClass('tall');
33113         return;
33114         
33115     },
33116     
33117     uploadFromSource : function(file, crop)
33118     {
33119         this.xhr = new XMLHttpRequest();
33120         
33121         this.managerEl.createChild({
33122             tag : 'div',
33123             cls : 'roo-document-manager-loading',
33124             cn : [
33125                 {
33126                     tag : 'div',
33127                     tooltip : file.name,
33128                     cls : 'roo-document-manager-thumb',
33129                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33130                 }
33131             ]
33132
33133         });
33134
33135         this.xhr.open(this.method, this.url, true);
33136         
33137         var headers = {
33138             "Accept": "application/json",
33139             "Cache-Control": "no-cache",
33140             "X-Requested-With": "XMLHttpRequest"
33141         };
33142         
33143         for (var headerName in headers) {
33144             var headerValue = headers[headerName];
33145             if (headerValue) {
33146                 this.xhr.setRequestHeader(headerName, headerValue);
33147             }
33148         }
33149         
33150         var _this = this;
33151         
33152         this.xhr.onload = function()
33153         {
33154             _this.xhrOnLoad(_this.xhr);
33155         }
33156         
33157         this.xhr.onerror = function()
33158         {
33159             _this.xhrOnError(_this.xhr);
33160         }
33161         
33162         var formData = new FormData();
33163
33164         formData.append('returnHTML', 'NO');
33165         
33166         formData.append('crop', crop);
33167         
33168         if(typeof(file.filename) != 'undefined'){
33169             formData.append('filename', file.filename);
33170         }
33171         
33172         if(typeof(file.mimetype) != 'undefined'){
33173             formData.append('mimetype', file.mimetype);
33174         }
33175         
33176         Roo.log(formData);
33177         
33178         if(this.fireEvent('prepare', this, formData) != false){
33179             this.xhr.send(formData);
33180         };
33181     }
33182 });
33183
33184 /*
33185 * Licence: LGPL
33186 */
33187
33188 /**
33189  * @class Roo.bootstrap.DocumentViewer
33190  * @extends Roo.bootstrap.Component
33191  * Bootstrap DocumentViewer class
33192  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33193  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33194  * 
33195  * @constructor
33196  * Create a new DocumentViewer
33197  * @param {Object} config The config object
33198  */
33199
33200 Roo.bootstrap.DocumentViewer = function(config){
33201     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33202     
33203     this.addEvents({
33204         /**
33205          * @event initial
33206          * Fire after initEvent
33207          * @param {Roo.bootstrap.DocumentViewer} this
33208          */
33209         "initial" : true,
33210         /**
33211          * @event click
33212          * Fire after click
33213          * @param {Roo.bootstrap.DocumentViewer} this
33214          */
33215         "click" : true,
33216         /**
33217          * @event download
33218          * Fire after download button
33219          * @param {Roo.bootstrap.DocumentViewer} this
33220          */
33221         "download" : true,
33222         /**
33223          * @event trash
33224          * Fire after trash button
33225          * @param {Roo.bootstrap.DocumentViewer} this
33226          */
33227         "trash" : true
33228         
33229     });
33230 };
33231
33232 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33233     
33234     showDownload : true,
33235     
33236     showTrash : true,
33237     
33238     getAutoCreate : function()
33239     {
33240         var cfg = {
33241             tag : 'div',
33242             cls : 'roo-document-viewer',
33243             cn : [
33244                 {
33245                     tag : 'div',
33246                     cls : 'roo-document-viewer-body',
33247                     cn : [
33248                         {
33249                             tag : 'div',
33250                             cls : 'roo-document-viewer-thumb',
33251                             cn : [
33252                                 {
33253                                     tag : 'img',
33254                                     cls : 'roo-document-viewer-image'
33255                                 }
33256                             ]
33257                         }
33258                     ]
33259                 },
33260                 {
33261                     tag : 'div',
33262                     cls : 'roo-document-viewer-footer',
33263                     cn : {
33264                         tag : 'div',
33265                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33266                         cn : [
33267                             {
33268                                 tag : 'div',
33269                                 cls : 'btn-group roo-document-viewer-download',
33270                                 cn : [
33271                                     {
33272                                         tag : 'button',
33273                                         cls : 'btn btn-default',
33274                                         html : '<i class="fa fa-download"></i>'
33275                                     }
33276                                 ]
33277                             },
33278                             {
33279                                 tag : 'div',
33280                                 cls : 'btn-group roo-document-viewer-trash',
33281                                 cn : [
33282                                     {
33283                                         tag : 'button',
33284                                         cls : 'btn btn-default',
33285                                         html : '<i class="fa fa-trash"></i>'
33286                                     }
33287                                 ]
33288                             }
33289                         ]
33290                     }
33291                 }
33292             ]
33293         };
33294         
33295         return cfg;
33296     },
33297     
33298     initEvents : function()
33299     {
33300         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33301         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33302         
33303         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33304         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33305         
33306         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33307         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33308         
33309         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33310         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33311         
33312         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33313         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33314         
33315         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33316         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33317         
33318         this.bodyEl.on('click', this.onClick, this);
33319         this.downloadBtn.on('click', this.onDownload, this);
33320         this.trashBtn.on('click', this.onTrash, this);
33321         
33322         this.downloadBtn.hide();
33323         this.trashBtn.hide();
33324         
33325         if(this.showDownload){
33326             this.downloadBtn.show();
33327         }
33328         
33329         if(this.showTrash){
33330             this.trashBtn.show();
33331         }
33332         
33333         if(!this.showDownload && !this.showTrash) {
33334             this.footerEl.hide();
33335         }
33336         
33337     },
33338     
33339     initial : function()
33340     {
33341         this.fireEvent('initial', this);
33342         
33343     },
33344     
33345     onClick : function(e)
33346     {
33347         e.preventDefault();
33348         
33349         this.fireEvent('click', this);
33350     },
33351     
33352     onDownload : function(e)
33353     {
33354         e.preventDefault();
33355         
33356         this.fireEvent('download', this);
33357     },
33358     
33359     onTrash : function(e)
33360     {
33361         e.preventDefault();
33362         
33363         this.fireEvent('trash', this);
33364     }
33365     
33366 });
33367 /*
33368  * - LGPL
33369  *
33370  * FieldLabel
33371  * 
33372  */
33373
33374 /**
33375  * @class Roo.bootstrap.form.FieldLabel
33376  * @extends Roo.bootstrap.Component
33377  * Bootstrap FieldLabel class
33378  * @cfg {String} html contents of the element
33379  * @cfg {String} tag tag of the element default label
33380  * @cfg {String} cls class of the element
33381  * @cfg {String} target label target 
33382  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33383  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33384  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33385  * @cfg {String} iconTooltip default "This field is required"
33386  * @cfg {String} indicatorpos (left|right) default left
33387  * 
33388  * @constructor
33389  * Create a new FieldLabel
33390  * @param {Object} config The config object
33391  */
33392
33393 Roo.bootstrap.form.FieldLabel = function(config){
33394     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33395     
33396     this.addEvents({
33397             /**
33398              * @event invalid
33399              * Fires after the field has been marked as invalid.
33400              * @param {Roo.form.FieldLabel} this
33401              * @param {String} msg The validation message
33402              */
33403             invalid : true,
33404             /**
33405              * @event valid
33406              * Fires after the field has been validated with no errors.
33407              * @param {Roo.form.FieldLabel} this
33408              */
33409             valid : true
33410         });
33411 };
33412
33413 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
33414     
33415     tag: 'label',
33416     cls: '',
33417     html: '',
33418     target: '',
33419     allowBlank : true,
33420     invalidClass : 'has-warning',
33421     validClass : 'has-success',
33422     iconTooltip : 'This field is required',
33423     indicatorpos : 'left',
33424     
33425     getAutoCreate : function(){
33426         
33427         var cls = "";
33428         if (!this.allowBlank) {
33429             cls  = "visible";
33430         }
33431         
33432         var cfg = {
33433             tag : this.tag,
33434             cls : 'roo-bootstrap-field-label ' + this.cls,
33435             for : this.target,
33436             cn : [
33437                 {
33438                     tag : 'i',
33439                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33440                     tooltip : this.iconTooltip
33441                 },
33442                 {
33443                     tag : 'span',
33444                     html : this.html
33445                 }
33446             ] 
33447         };
33448         
33449         if(this.indicatorpos == 'right'){
33450             var cfg = {
33451                 tag : this.tag,
33452                 cls : 'roo-bootstrap-field-label ' + this.cls,
33453                 for : this.target,
33454                 cn : [
33455                     {
33456                         tag : 'span',
33457                         html : this.html
33458                     },
33459                     {
33460                         tag : 'i',
33461                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33462                         tooltip : this.iconTooltip
33463                     }
33464                 ] 
33465             };
33466         }
33467         
33468         return cfg;
33469     },
33470     
33471     initEvents: function() 
33472     {
33473         Roo.bootstrap.Element.superclass.initEvents.call(this);
33474         
33475         this.indicator = this.indicatorEl();
33476         
33477         if(this.indicator){
33478             this.indicator.removeClass('visible');
33479             this.indicator.addClass('invisible');
33480         }
33481         
33482         Roo.bootstrap.form.FieldLabel.register(this);
33483     },
33484     
33485     indicatorEl : function()
33486     {
33487         var indicator = this.el.select('i.roo-required-indicator',true).first();
33488         
33489         if(!indicator){
33490             return false;
33491         }
33492         
33493         return indicator;
33494         
33495     },
33496     
33497     /**
33498      * Mark this field as valid
33499      */
33500     markValid : function()
33501     {
33502         if(this.indicator){
33503             this.indicator.removeClass('visible');
33504             this.indicator.addClass('invisible');
33505         }
33506         if (Roo.bootstrap.version == 3) {
33507             this.el.removeClass(this.invalidClass);
33508             this.el.addClass(this.validClass);
33509         } else {
33510             this.el.removeClass('is-invalid');
33511             this.el.addClass('is-valid');
33512         }
33513         
33514         
33515         this.fireEvent('valid', this);
33516     },
33517     
33518     /**
33519      * Mark this field as invalid
33520      * @param {String} msg The validation message
33521      */
33522     markInvalid : function(msg)
33523     {
33524         if(this.indicator){
33525             this.indicator.removeClass('invisible');
33526             this.indicator.addClass('visible');
33527         }
33528           if (Roo.bootstrap.version == 3) {
33529             this.el.removeClass(this.validClass);
33530             this.el.addClass(this.invalidClass);
33531         } else {
33532             this.el.removeClass('is-valid');
33533             this.el.addClass('is-invalid');
33534         }
33535         
33536         
33537         this.fireEvent('invalid', this, msg);
33538     }
33539     
33540    
33541 });
33542
33543 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33544     
33545     groups: {},
33546     
33547      /**
33548     * register a FieldLabel Group
33549     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33550     */
33551     register : function(label)
33552     {
33553         if(this.groups.hasOwnProperty(label.target)){
33554             return;
33555         }
33556      
33557         this.groups[label.target] = label;
33558         
33559     },
33560     /**
33561     * fetch a FieldLabel Group based on the target
33562     * @param {string} target
33563     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33564     */
33565     get: function(target) {
33566         if (typeof(this.groups[target]) == 'undefined') {
33567             return false;
33568         }
33569         
33570         return this.groups[target] ;
33571     }
33572 });
33573
33574  
33575
33576  /*
33577  * - LGPL
33578  *
33579  * page DateSplitField.
33580  * 
33581  */
33582
33583
33584 /**
33585  * @class Roo.bootstrap.form.DateSplitField
33586  * @extends Roo.bootstrap.Component
33587  * Bootstrap DateSplitField class
33588  * @cfg {string} fieldLabel - the label associated
33589  * @cfg {Number} labelWidth set the width of label (0-12)
33590  * @cfg {String} labelAlign (top|left)
33591  * @cfg {Boolean} dayAllowBlank (true|false) default false
33592  * @cfg {Boolean} monthAllowBlank (true|false) default false
33593  * @cfg {Boolean} yearAllowBlank (true|false) default false
33594  * @cfg {string} dayPlaceholder 
33595  * @cfg {string} monthPlaceholder
33596  * @cfg {string} yearPlaceholder
33597  * @cfg {string} dayFormat default 'd'
33598  * @cfg {string} monthFormat default 'm'
33599  * @cfg {string} yearFormat default 'Y'
33600  * @cfg {Number} labellg set the width of label (1-12)
33601  * @cfg {Number} labelmd set the width of label (1-12)
33602  * @cfg {Number} labelsm set the width of label (1-12)
33603  * @cfg {Number} labelxs set the width of label (1-12)
33604
33605  *     
33606  * @constructor
33607  * Create a new DateSplitField
33608  * @param {Object} config The config object
33609  */
33610
33611 Roo.bootstrap.form.DateSplitField = function(config){
33612     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33613     
33614     this.addEvents({
33615         // raw events
33616          /**
33617          * @event years
33618          * getting the data of years
33619          * @param {Roo.bootstrap.form.DateSplitField} this
33620          * @param {Object} years
33621          */
33622         "years" : true,
33623         /**
33624          * @event days
33625          * getting the data of days
33626          * @param {Roo.bootstrap.form.DateSplitField} this
33627          * @param {Object} days
33628          */
33629         "days" : true,
33630         /**
33631          * @event invalid
33632          * Fires after the field has been marked as invalid.
33633          * @param {Roo.form.Field} this
33634          * @param {String} msg The validation message
33635          */
33636         invalid : true,
33637        /**
33638          * @event valid
33639          * Fires after the field has been validated with no errors.
33640          * @param {Roo.form.Field} this
33641          */
33642         valid : true
33643     });
33644 };
33645
33646 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
33647     
33648     fieldLabel : '',
33649     labelAlign : 'top',
33650     labelWidth : 3,
33651     dayAllowBlank : false,
33652     monthAllowBlank : false,
33653     yearAllowBlank : false,
33654     dayPlaceholder : '',
33655     monthPlaceholder : '',
33656     yearPlaceholder : '',
33657     dayFormat : 'd',
33658     monthFormat : 'm',
33659     yearFormat : 'Y',
33660     isFormField : true,
33661     labellg : 0,
33662     labelmd : 0,
33663     labelsm : 0,
33664     labelxs : 0,
33665     
33666     getAutoCreate : function()
33667     {
33668         var cfg = {
33669             tag : 'div',
33670             cls : 'row roo-date-split-field-group',
33671             cn : [
33672                 {
33673                     tag : 'input',
33674                     type : 'hidden',
33675                     cls : 'form-hidden-field roo-date-split-field-group-value',
33676                     name : this.name
33677                 }
33678             ]
33679         };
33680         
33681         var labelCls = 'col-md-12';
33682         var contentCls = 'col-md-4';
33683         
33684         if(this.fieldLabel){
33685             
33686             var label = {
33687                 tag : 'div',
33688                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33689                 cn : [
33690                     {
33691                         tag : 'label',
33692                         html : this.fieldLabel
33693                     }
33694                 ]
33695             };
33696             
33697             if(this.labelAlign == 'left'){
33698             
33699                 if(this.labelWidth > 12){
33700                     label.style = "width: " + this.labelWidth + 'px';
33701                 }
33702
33703                 if(this.labelWidth < 13 && this.labelmd == 0){
33704                     this.labelmd = this.labelWidth;
33705                 }
33706
33707                 if(this.labellg > 0){
33708                     labelCls = ' col-lg-' + this.labellg;
33709                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33710                 }
33711
33712                 if(this.labelmd > 0){
33713                     labelCls = ' col-md-' + this.labelmd;
33714                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33715                 }
33716
33717                 if(this.labelsm > 0){
33718                     labelCls = ' col-sm-' + this.labelsm;
33719                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33720                 }
33721
33722                 if(this.labelxs > 0){
33723                     labelCls = ' col-xs-' + this.labelxs;
33724                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33725                 }
33726             }
33727             
33728             label.cls += ' ' + labelCls;
33729             
33730             cfg.cn.push(label);
33731         }
33732         
33733         Roo.each(['day', 'month', 'year'], function(t){
33734             cfg.cn.push({
33735                 tag : 'div',
33736                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33737             });
33738         }, this);
33739         
33740         return cfg;
33741     },
33742     
33743     inputEl: function ()
33744     {
33745         return this.el.select('.roo-date-split-field-group-value', true).first();
33746     },
33747     
33748     onRender : function(ct, position) 
33749     {
33750         var _this = this;
33751         
33752         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33753         
33754         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33755         
33756         this.dayField = new Roo.bootstrap.form.ComboBox({
33757             allowBlank : this.dayAllowBlank,
33758             alwaysQuery : true,
33759             displayField : 'value',
33760             editable : false,
33761             fieldLabel : '',
33762             forceSelection : true,
33763             mode : 'local',
33764             placeholder : this.dayPlaceholder,
33765             selectOnFocus : true,
33766             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33767             triggerAction : 'all',
33768             typeAhead : true,
33769             valueField : 'value',
33770             store : new Roo.data.SimpleStore({
33771                 data : (function() {    
33772                     var days = [];
33773                     _this.fireEvent('days', _this, days);
33774                     return days;
33775                 })(),
33776                 fields : [ 'value' ]
33777             }),
33778             listeners : {
33779                 select : function (_self, record, index)
33780                 {
33781                     _this.setValue(_this.getValue());
33782                 }
33783             }
33784         });
33785
33786         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33787         
33788         this.monthField = new Roo.bootstrap.form.MonthField({
33789             after : '<i class=\"fa fa-calendar\"></i>',
33790             allowBlank : this.monthAllowBlank,
33791             placeholder : this.monthPlaceholder,
33792             readOnly : true,
33793             listeners : {
33794                 render : function (_self)
33795                 {
33796                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33797                         e.preventDefault();
33798                         _self.focus();
33799                     });
33800                 },
33801                 select : function (_self, oldvalue, newvalue)
33802                 {
33803                     _this.setValue(_this.getValue());
33804                 }
33805             }
33806         });
33807         
33808         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33809         
33810         this.yearField = new Roo.bootstrap.form.ComboBox({
33811             allowBlank : this.yearAllowBlank,
33812             alwaysQuery : true,
33813             displayField : 'value',
33814             editable : false,
33815             fieldLabel : '',
33816             forceSelection : true,
33817             mode : 'local',
33818             placeholder : this.yearPlaceholder,
33819             selectOnFocus : true,
33820             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33821             triggerAction : 'all',
33822             typeAhead : true,
33823             valueField : 'value',
33824             store : new Roo.data.SimpleStore({
33825                 data : (function() {
33826                     var years = [];
33827                     _this.fireEvent('years', _this, years);
33828                     return years;
33829                 })(),
33830                 fields : [ 'value' ]
33831             }),
33832             listeners : {
33833                 select : function (_self, record, index)
33834                 {
33835                     _this.setValue(_this.getValue());
33836                 }
33837             }
33838         });
33839
33840         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33841     },
33842     
33843     setValue : function(v, format)
33844     {
33845         this.inputEl.dom.value = v;
33846         
33847         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33848         
33849         var d = Date.parseDate(v, f);
33850         
33851         if(!d){
33852             this.validate();
33853             return;
33854         }
33855         
33856         this.setDay(d.format(this.dayFormat));
33857         this.setMonth(d.format(this.monthFormat));
33858         this.setYear(d.format(this.yearFormat));
33859         
33860         this.validate();
33861         
33862         return;
33863     },
33864     
33865     setDay : function(v)
33866     {
33867         this.dayField.setValue(v);
33868         this.inputEl.dom.value = this.getValue();
33869         this.validate();
33870         return;
33871     },
33872     
33873     setMonth : function(v)
33874     {
33875         this.monthField.setValue(v, true);
33876         this.inputEl.dom.value = this.getValue();
33877         this.validate();
33878         return;
33879     },
33880     
33881     setYear : function(v)
33882     {
33883         this.yearField.setValue(v);
33884         this.inputEl.dom.value = this.getValue();
33885         this.validate();
33886         return;
33887     },
33888     
33889     getDay : function()
33890     {
33891         return this.dayField.getValue();
33892     },
33893     
33894     getMonth : function()
33895     {
33896         return this.monthField.getValue();
33897     },
33898     
33899     getYear : function()
33900     {
33901         return this.yearField.getValue();
33902     },
33903     
33904     getValue : function()
33905     {
33906         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33907         
33908         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33909         
33910         return date;
33911     },
33912     
33913     reset : function()
33914     {
33915         this.setDay('');
33916         this.setMonth('');
33917         this.setYear('');
33918         this.inputEl.dom.value = '';
33919         this.validate();
33920         return;
33921     },
33922     
33923     validate : function()
33924     {
33925         var d = this.dayField.validate();
33926         var m = this.monthField.validate();
33927         var y = this.yearField.validate();
33928         
33929         var valid = true;
33930         
33931         if(
33932                 (!this.dayAllowBlank && !d) ||
33933                 (!this.monthAllowBlank && !m) ||
33934                 (!this.yearAllowBlank && !y)
33935         ){
33936             valid = false;
33937         }
33938         
33939         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33940             return valid;
33941         }
33942         
33943         if(valid){
33944             this.markValid();
33945             return valid;
33946         }
33947         
33948         this.markInvalid();
33949         
33950         return valid;
33951     },
33952     
33953     markValid : function()
33954     {
33955         
33956         var label = this.el.select('label', true).first();
33957         var icon = this.el.select('i.fa-star', true).first();
33958
33959         if(label && icon){
33960             icon.remove();
33961         }
33962         
33963         this.fireEvent('valid', this);
33964     },
33965     
33966      /**
33967      * Mark this field as invalid
33968      * @param {String} msg The validation message
33969      */
33970     markInvalid : function(msg)
33971     {
33972         
33973         var label = this.el.select('label', true).first();
33974         var icon = this.el.select('i.fa-star', true).first();
33975
33976         if(label && !icon){
33977             this.el.select('.roo-date-split-field-label', true).createChild({
33978                 tag : 'i',
33979                 cls : 'text-danger fa fa-lg fa-star',
33980                 tooltip : 'This field is required',
33981                 style : 'margin-right:5px;'
33982             }, label, true);
33983         }
33984         
33985         this.fireEvent('invalid', this, msg);
33986     },
33987     
33988     clearInvalid : function()
33989     {
33990         var label = this.el.select('label', true).first();
33991         var icon = this.el.select('i.fa-star', true).first();
33992
33993         if(label && icon){
33994             icon.remove();
33995         }
33996         
33997         this.fireEvent('valid', this);
33998     },
33999     
34000     getName: function()
34001     {
34002         return this.name;
34003     }
34004     
34005 });
34006
34007  
34008
34009 /**
34010  * @class Roo.bootstrap.LayoutMasonry
34011  * @extends Roo.bootstrap.Component
34012  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34013  * Bootstrap Layout Masonry class
34014  *
34015  * This is based on 
34016  * http://masonry.desandro.com
34017  *
34018  * The idea is to render all the bricks based on vertical width...
34019  *
34020  * The original code extends 'outlayer' - we might need to use that....
34021
34022  * @constructor
34023  * Create a new Element
34024  * @param {Object} config The config object
34025  */
34026
34027 Roo.bootstrap.LayoutMasonry = function(config){
34028     
34029     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34030     
34031     this.bricks = [];
34032     
34033     Roo.bootstrap.LayoutMasonry.register(this);
34034     
34035     this.addEvents({
34036         // raw events
34037         /**
34038          * @event layout
34039          * Fire after layout the items
34040          * @param {Roo.bootstrap.LayoutMasonry} this
34041          * @param {Roo.EventObject} e
34042          */
34043         "layout" : true
34044     });
34045     
34046 };
34047
34048 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34049     
34050     /**
34051      * @cfg {Boolean} isLayoutInstant = no animation?
34052      */   
34053     isLayoutInstant : false, // needed?
34054    
34055     /**
34056      * @cfg {Number} boxWidth  width of the columns
34057      */   
34058     boxWidth : 450,
34059     
34060       /**
34061      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34062      */   
34063     boxHeight : 0,
34064     
34065     /**
34066      * @cfg {Number} padWidth padding below box..
34067      */   
34068     padWidth : 10, 
34069     
34070     /**
34071      * @cfg {Number} gutter gutter width..
34072      */   
34073     gutter : 10,
34074     
34075      /**
34076      * @cfg {Number} maxCols maximum number of columns
34077      */   
34078     
34079     maxCols: 0,
34080     
34081     /**
34082      * @cfg {Boolean} isAutoInitial defalut true
34083      */   
34084     isAutoInitial : true, 
34085     
34086     containerWidth: 0,
34087     
34088     /**
34089      * @cfg {Boolean} isHorizontal defalut false
34090      */   
34091     isHorizontal : false, 
34092
34093     currentSize : null,
34094     
34095     tag: 'div',
34096     
34097     cls: '',
34098     
34099     bricks: null, //CompositeElement
34100     
34101     cols : 1,
34102     
34103     _isLayoutInited : false,
34104     
34105 //    isAlternative : false, // only use for vertical layout...
34106     
34107     /**
34108      * @cfg {Number} alternativePadWidth padding below box..
34109      */   
34110     alternativePadWidth : 50,
34111     
34112     selectedBrick : [],
34113     
34114     getAutoCreate : function(){
34115         
34116         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34117         
34118         var cfg = {
34119             tag: this.tag,
34120             cls: 'blog-masonary-wrapper ' + this.cls,
34121             cn : {
34122                 cls : 'mas-boxes masonary'
34123             }
34124         };
34125         
34126         return cfg;
34127     },
34128     
34129     getChildContainer: function( )
34130     {
34131         if (this.boxesEl) {
34132             return this.boxesEl;
34133         }
34134         
34135         this.boxesEl = this.el.select('.mas-boxes').first();
34136         
34137         return this.boxesEl;
34138     },
34139     
34140     
34141     initEvents : function()
34142     {
34143         var _this = this;
34144         
34145         if(this.isAutoInitial){
34146             Roo.log('hook children rendered');
34147             this.on('childrenrendered', function() {
34148                 Roo.log('children rendered');
34149                 _this.initial();
34150             } ,this);
34151         }
34152     },
34153     
34154     initial : function()
34155     {
34156         this.selectedBrick = [];
34157         
34158         this.currentSize = this.el.getBox(true);
34159         
34160         Roo.EventManager.onWindowResize(this.resize, this); 
34161
34162         if(!this.isAutoInitial){
34163             this.layout();
34164             return;
34165         }
34166         
34167         this.layout();
34168         
34169         return;
34170         //this.layout.defer(500,this);
34171         
34172     },
34173     
34174     resize : function()
34175     {
34176         var cs = this.el.getBox(true);
34177         
34178         if (
34179                 this.currentSize.width == cs.width && 
34180                 this.currentSize.x == cs.x && 
34181                 this.currentSize.height == cs.height && 
34182                 this.currentSize.y == cs.y 
34183         ) {
34184             Roo.log("no change in with or X or Y");
34185             return;
34186         }
34187         
34188         this.currentSize = cs;
34189         
34190         this.layout();
34191         
34192     },
34193     
34194     layout : function()
34195     {   
34196         this._resetLayout();
34197         
34198         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34199         
34200         this.layoutItems( isInstant );
34201       
34202         this._isLayoutInited = true;
34203         
34204         this.fireEvent('layout', this);
34205         
34206     },
34207     
34208     _resetLayout : function()
34209     {
34210         if(this.isHorizontal){
34211             this.horizontalMeasureColumns();
34212             return;
34213         }
34214         
34215         this.verticalMeasureColumns();
34216         
34217     },
34218     
34219     verticalMeasureColumns : function()
34220     {
34221         this.getContainerWidth();
34222         
34223 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34224 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34225 //            return;
34226 //        }
34227         
34228         var boxWidth = this.boxWidth + this.padWidth;
34229         
34230         if(this.containerWidth < this.boxWidth){
34231             boxWidth = this.containerWidth
34232         }
34233         
34234         var containerWidth = this.containerWidth;
34235         
34236         var cols = Math.floor(containerWidth / boxWidth);
34237         
34238         this.cols = Math.max( cols, 1 );
34239         
34240         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34241         
34242         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34243         
34244         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34245         
34246         this.colWidth = boxWidth + avail - this.padWidth;
34247         
34248         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34249         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34250     },
34251     
34252     horizontalMeasureColumns : function()
34253     {
34254         this.getContainerWidth();
34255         
34256         var boxWidth = this.boxWidth;
34257         
34258         if(this.containerWidth < boxWidth){
34259             boxWidth = this.containerWidth;
34260         }
34261         
34262         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34263         
34264         this.el.setHeight(boxWidth);
34265         
34266     },
34267     
34268     getContainerWidth : function()
34269     {
34270         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34271     },
34272     
34273     layoutItems : function( isInstant )
34274     {
34275         Roo.log(this.bricks);
34276         
34277         var items = Roo.apply([], this.bricks);
34278         
34279         if(this.isHorizontal){
34280             this._horizontalLayoutItems( items , isInstant );
34281             return;
34282         }
34283         
34284 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34285 //            this._verticalAlternativeLayoutItems( items , isInstant );
34286 //            return;
34287 //        }
34288         
34289         this._verticalLayoutItems( items , isInstant );
34290         
34291     },
34292     
34293     _verticalLayoutItems : function ( items , isInstant)
34294     {
34295         if ( !items || !items.length ) {
34296             return;
34297         }
34298         
34299         var standard = [
34300             ['xs', 'xs', 'xs', 'tall'],
34301             ['xs', 'xs', 'tall'],
34302             ['xs', 'xs', 'sm'],
34303             ['xs', 'xs', 'xs'],
34304             ['xs', 'tall'],
34305             ['xs', 'sm'],
34306             ['xs', 'xs'],
34307             ['xs'],
34308             
34309             ['sm', 'xs', 'xs'],
34310             ['sm', 'xs'],
34311             ['sm'],
34312             
34313             ['tall', 'xs', 'xs', 'xs'],
34314             ['tall', 'xs', 'xs'],
34315             ['tall', 'xs'],
34316             ['tall']
34317             
34318         ];
34319         
34320         var queue = [];
34321         
34322         var boxes = [];
34323         
34324         var box = [];
34325         
34326         Roo.each(items, function(item, k){
34327             
34328             switch (item.size) {
34329                 // these layouts take up a full box,
34330                 case 'md' :
34331                 case 'md-left' :
34332                 case 'md-right' :
34333                 case 'wide' :
34334                     
34335                     if(box.length){
34336                         boxes.push(box);
34337                         box = [];
34338                     }
34339                     
34340                     boxes.push([item]);
34341                     
34342                     break;
34343                     
34344                 case 'xs' :
34345                 case 'sm' :
34346                 case 'tall' :
34347                     
34348                     box.push(item);
34349                     
34350                     break;
34351                 default :
34352                     break;
34353                     
34354             }
34355             
34356         }, this);
34357         
34358         if(box.length){
34359             boxes.push(box);
34360             box = [];
34361         }
34362         
34363         var filterPattern = function(box, length)
34364         {
34365             if(!box.length){
34366                 return;
34367             }
34368             
34369             var match = false;
34370             
34371             var pattern = box.slice(0, length);
34372             
34373             var format = [];
34374             
34375             Roo.each(pattern, function(i){
34376                 format.push(i.size);
34377             }, this);
34378             
34379             Roo.each(standard, function(s){
34380                 
34381                 if(String(s) != String(format)){
34382                     return;
34383                 }
34384                 
34385                 match = true;
34386                 return false;
34387                 
34388             }, this);
34389             
34390             if(!match && length == 1){
34391                 return;
34392             }
34393             
34394             if(!match){
34395                 filterPattern(box, length - 1);
34396                 return;
34397             }
34398                 
34399             queue.push(pattern);
34400
34401             box = box.slice(length, box.length);
34402
34403             filterPattern(box, 4);
34404
34405             return;
34406             
34407         }
34408         
34409         Roo.each(boxes, function(box, k){
34410             
34411             if(!box.length){
34412                 return;
34413             }
34414             
34415             if(box.length == 1){
34416                 queue.push(box);
34417                 return;
34418             }
34419             
34420             filterPattern(box, 4);
34421             
34422         }, this);
34423         
34424         this._processVerticalLayoutQueue( queue, isInstant );
34425         
34426     },
34427     
34428 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34429 //    {
34430 //        if ( !items || !items.length ) {
34431 //            return;
34432 //        }
34433 //
34434 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34435 //        
34436 //    },
34437     
34438     _horizontalLayoutItems : function ( items , isInstant)
34439     {
34440         if ( !items || !items.length || items.length < 3) {
34441             return;
34442         }
34443         
34444         items.reverse();
34445         
34446         var eItems = items.slice(0, 3);
34447         
34448         items = items.slice(3, items.length);
34449         
34450         var standard = [
34451             ['xs', 'xs', 'xs', 'wide'],
34452             ['xs', 'xs', 'wide'],
34453             ['xs', 'xs', 'sm'],
34454             ['xs', 'xs', 'xs'],
34455             ['xs', 'wide'],
34456             ['xs', 'sm'],
34457             ['xs', 'xs'],
34458             ['xs'],
34459             
34460             ['sm', 'xs', 'xs'],
34461             ['sm', 'xs'],
34462             ['sm'],
34463             
34464             ['wide', 'xs', 'xs', 'xs'],
34465             ['wide', 'xs', 'xs'],
34466             ['wide', 'xs'],
34467             ['wide'],
34468             
34469             ['wide-thin']
34470         ];
34471         
34472         var queue = [];
34473         
34474         var boxes = [];
34475         
34476         var box = [];
34477         
34478         Roo.each(items, function(item, k){
34479             
34480             switch (item.size) {
34481                 case 'md' :
34482                 case 'md-left' :
34483                 case 'md-right' :
34484                 case 'tall' :
34485                     
34486                     if(box.length){
34487                         boxes.push(box);
34488                         box = [];
34489                     }
34490                     
34491                     boxes.push([item]);
34492                     
34493                     break;
34494                     
34495                 case 'xs' :
34496                 case 'sm' :
34497                 case 'wide' :
34498                 case 'wide-thin' :
34499                     
34500                     box.push(item);
34501                     
34502                     break;
34503                 default :
34504                     break;
34505                     
34506             }
34507             
34508         }, this);
34509         
34510         if(box.length){
34511             boxes.push(box);
34512             box = [];
34513         }
34514         
34515         var filterPattern = function(box, length)
34516         {
34517             if(!box.length){
34518                 return;
34519             }
34520             
34521             var match = false;
34522             
34523             var pattern = box.slice(0, length);
34524             
34525             var format = [];
34526             
34527             Roo.each(pattern, function(i){
34528                 format.push(i.size);
34529             }, this);
34530             
34531             Roo.each(standard, function(s){
34532                 
34533                 if(String(s) != String(format)){
34534                     return;
34535                 }
34536                 
34537                 match = true;
34538                 return false;
34539                 
34540             }, this);
34541             
34542             if(!match && length == 1){
34543                 return;
34544             }
34545             
34546             if(!match){
34547                 filterPattern(box, length - 1);
34548                 return;
34549             }
34550                 
34551             queue.push(pattern);
34552
34553             box = box.slice(length, box.length);
34554
34555             filterPattern(box, 4);
34556
34557             return;
34558             
34559         }
34560         
34561         Roo.each(boxes, function(box, k){
34562             
34563             if(!box.length){
34564                 return;
34565             }
34566             
34567             if(box.length == 1){
34568                 queue.push(box);
34569                 return;
34570             }
34571             
34572             filterPattern(box, 4);
34573             
34574         }, this);
34575         
34576         
34577         var prune = [];
34578         
34579         var pos = this.el.getBox(true);
34580         
34581         var minX = pos.x;
34582         
34583         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34584         
34585         var hit_end = false;
34586         
34587         Roo.each(queue, function(box){
34588             
34589             if(hit_end){
34590                 
34591                 Roo.each(box, function(b){
34592                 
34593                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34594                     b.el.hide();
34595
34596                 }, this);
34597
34598                 return;
34599             }
34600             
34601             var mx = 0;
34602             
34603             Roo.each(box, function(b){
34604                 
34605                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34606                 b.el.show();
34607
34608                 mx = Math.max(mx, b.x);
34609                 
34610             }, this);
34611             
34612             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34613             
34614             if(maxX < minX){
34615                 
34616                 Roo.each(box, function(b){
34617                 
34618                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34619                     b.el.hide();
34620                     
34621                 }, this);
34622                 
34623                 hit_end = true;
34624                 
34625                 return;
34626             }
34627             
34628             prune.push(box);
34629             
34630         }, this);
34631         
34632         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34633     },
34634     
34635     /** Sets position of item in DOM
34636     * @param {Element} item
34637     * @param {Number} x - horizontal position
34638     * @param {Number} y - vertical position
34639     * @param {Boolean} isInstant - disables transitions
34640     */
34641     _processVerticalLayoutQueue : function( queue, isInstant )
34642     {
34643         var pos = this.el.getBox(true);
34644         var x = pos.x;
34645         var y = pos.y;
34646         var maxY = [];
34647         
34648         for (var i = 0; i < this.cols; i++){
34649             maxY[i] = pos.y;
34650         }
34651         
34652         Roo.each(queue, function(box, k){
34653             
34654             var col = k % this.cols;
34655             
34656             Roo.each(box, function(b,kk){
34657                 
34658                 b.el.position('absolute');
34659                 
34660                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34661                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34662                 
34663                 if(b.size == 'md-left' || b.size == 'md-right'){
34664                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34665                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34666                 }
34667                 
34668                 b.el.setWidth(width);
34669                 b.el.setHeight(height);
34670                 // iframe?
34671                 b.el.select('iframe',true).setSize(width,height);
34672                 
34673             }, this);
34674             
34675             for (var i = 0; i < this.cols; i++){
34676                 
34677                 if(maxY[i] < maxY[col]){
34678                     col = i;
34679                     continue;
34680                 }
34681                 
34682                 col = Math.min(col, i);
34683                 
34684             }
34685             
34686             x = pos.x + col * (this.colWidth + this.padWidth);
34687             
34688             y = maxY[col];
34689             
34690             var positions = [];
34691             
34692             switch (box.length){
34693                 case 1 :
34694                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34695                     break;
34696                 case 2 :
34697                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34698                     break;
34699                 case 3 :
34700                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34701                     break;
34702                 case 4 :
34703                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34704                     break;
34705                 default :
34706                     break;
34707             }
34708             
34709             Roo.each(box, function(b,kk){
34710                 
34711                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34712                 
34713                 var sz = b.el.getSize();
34714                 
34715                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34716                 
34717             }, this);
34718             
34719         }, this);
34720         
34721         var mY = 0;
34722         
34723         for (var i = 0; i < this.cols; i++){
34724             mY = Math.max(mY, maxY[i]);
34725         }
34726         
34727         this.el.setHeight(mY - pos.y);
34728         
34729     },
34730     
34731 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34732 //    {
34733 //        var pos = this.el.getBox(true);
34734 //        var x = pos.x;
34735 //        var y = pos.y;
34736 //        var maxX = pos.right;
34737 //        
34738 //        var maxHeight = 0;
34739 //        
34740 //        Roo.each(items, function(item, k){
34741 //            
34742 //            var c = k % 2;
34743 //            
34744 //            item.el.position('absolute');
34745 //                
34746 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34747 //
34748 //            item.el.setWidth(width);
34749 //
34750 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34751 //
34752 //            item.el.setHeight(height);
34753 //            
34754 //            if(c == 0){
34755 //                item.el.setXY([x, y], isInstant ? false : true);
34756 //            } else {
34757 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34758 //            }
34759 //            
34760 //            y = y + height + this.alternativePadWidth;
34761 //            
34762 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34763 //            
34764 //        }, this);
34765 //        
34766 //        this.el.setHeight(maxHeight);
34767 //        
34768 //    },
34769     
34770     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34771     {
34772         var pos = this.el.getBox(true);
34773         
34774         var minX = pos.x;
34775         var minY = pos.y;
34776         
34777         var maxX = pos.right;
34778         
34779         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34780         
34781         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34782         
34783         Roo.each(queue, function(box, k){
34784             
34785             Roo.each(box, function(b, kk){
34786                 
34787                 b.el.position('absolute');
34788                 
34789                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34790                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34791                 
34792                 if(b.size == 'md-left' || b.size == 'md-right'){
34793                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34794                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34795                 }
34796                 
34797                 b.el.setWidth(width);
34798                 b.el.setHeight(height);
34799                 
34800             }, this);
34801             
34802             if(!box.length){
34803                 return;
34804             }
34805             
34806             var positions = [];
34807             
34808             switch (box.length){
34809                 case 1 :
34810                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34811                     break;
34812                 case 2 :
34813                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34814                     break;
34815                 case 3 :
34816                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34817                     break;
34818                 case 4 :
34819                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34820                     break;
34821                 default :
34822                     break;
34823             }
34824             
34825             Roo.each(box, function(b,kk){
34826                 
34827                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34828                 
34829                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34830                 
34831             }, this);
34832             
34833         }, this);
34834         
34835     },
34836     
34837     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34838     {
34839         Roo.each(eItems, function(b,k){
34840             
34841             b.size = (k == 0) ? 'sm' : 'xs';
34842             b.x = (k == 0) ? 2 : 1;
34843             b.y = (k == 0) ? 2 : 1;
34844             
34845             b.el.position('absolute');
34846             
34847             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34848                 
34849             b.el.setWidth(width);
34850             
34851             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34852             
34853             b.el.setHeight(height);
34854             
34855         }, this);
34856
34857         var positions = [];
34858         
34859         positions.push({
34860             x : maxX - this.unitWidth * 2 - this.gutter,
34861             y : minY
34862         });
34863         
34864         positions.push({
34865             x : maxX - this.unitWidth,
34866             y : minY + (this.unitWidth + this.gutter) * 2
34867         });
34868         
34869         positions.push({
34870             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34871             y : minY
34872         });
34873         
34874         Roo.each(eItems, function(b,k){
34875             
34876             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34877
34878         }, this);
34879         
34880     },
34881     
34882     getVerticalOneBoxColPositions : function(x, y, box)
34883     {
34884         var pos = [];
34885         
34886         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34887         
34888         if(box[0].size == 'md-left'){
34889             rand = 0;
34890         }
34891         
34892         if(box[0].size == 'md-right'){
34893             rand = 1;
34894         }
34895         
34896         pos.push({
34897             x : x + (this.unitWidth + this.gutter) * rand,
34898             y : y
34899         });
34900         
34901         return pos;
34902     },
34903     
34904     getVerticalTwoBoxColPositions : function(x, y, box)
34905     {
34906         var pos = [];
34907         
34908         if(box[0].size == 'xs'){
34909             
34910             pos.push({
34911                 x : x,
34912                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34913             });
34914
34915             pos.push({
34916                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34917                 y : y
34918             });
34919             
34920             return pos;
34921             
34922         }
34923         
34924         pos.push({
34925             x : x,
34926             y : y
34927         });
34928
34929         pos.push({
34930             x : x + (this.unitWidth + this.gutter) * 2,
34931             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34932         });
34933         
34934         return pos;
34935         
34936     },
34937     
34938     getVerticalThreeBoxColPositions : function(x, y, box)
34939     {
34940         var pos = [];
34941         
34942         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34943             
34944             pos.push({
34945                 x : x,
34946                 y : y
34947             });
34948
34949             pos.push({
34950                 x : x + (this.unitWidth + this.gutter) * 1,
34951                 y : y
34952             });
34953             
34954             pos.push({
34955                 x : x + (this.unitWidth + this.gutter) * 2,
34956                 y : y
34957             });
34958             
34959             return pos;
34960             
34961         }
34962         
34963         if(box[0].size == 'xs' && box[1].size == 'xs'){
34964             
34965             pos.push({
34966                 x : x,
34967                 y : y
34968             });
34969
34970             pos.push({
34971                 x : x,
34972                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34973             });
34974             
34975             pos.push({
34976                 x : x + (this.unitWidth + this.gutter) * 1,
34977                 y : y
34978             });
34979             
34980             return pos;
34981             
34982         }
34983         
34984         pos.push({
34985             x : x,
34986             y : y
34987         });
34988
34989         pos.push({
34990             x : x + (this.unitWidth + this.gutter) * 2,
34991             y : y
34992         });
34993
34994         pos.push({
34995             x : x + (this.unitWidth + this.gutter) * 2,
34996             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34997         });
34998             
34999         return pos;
35000         
35001     },
35002     
35003     getVerticalFourBoxColPositions : function(x, y, box)
35004     {
35005         var pos = [];
35006         
35007         if(box[0].size == 'xs'){
35008             
35009             pos.push({
35010                 x : x,
35011                 y : y
35012             });
35013
35014             pos.push({
35015                 x : x,
35016                 y : y + (this.unitHeight + this.gutter) * 1
35017             });
35018             
35019             pos.push({
35020                 x : x,
35021                 y : y + (this.unitHeight + this.gutter) * 2
35022             });
35023             
35024             pos.push({
35025                 x : x + (this.unitWidth + this.gutter) * 1,
35026                 y : y
35027             });
35028             
35029             return pos;
35030             
35031         }
35032         
35033         pos.push({
35034             x : x,
35035             y : y
35036         });
35037
35038         pos.push({
35039             x : x + (this.unitWidth + this.gutter) * 2,
35040             y : y
35041         });
35042
35043         pos.push({
35044             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35045             y : y + (this.unitHeight + this.gutter) * 1
35046         });
35047
35048         pos.push({
35049             x : x + (this.unitWidth + this.gutter) * 2,
35050             y : y + (this.unitWidth + this.gutter) * 2
35051         });
35052
35053         return pos;
35054         
35055     },
35056     
35057     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35058     {
35059         var pos = [];
35060         
35061         if(box[0].size == 'md-left'){
35062             pos.push({
35063                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35064                 y : minY
35065             });
35066             
35067             return pos;
35068         }
35069         
35070         if(box[0].size == 'md-right'){
35071             pos.push({
35072                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35073                 y : minY + (this.unitWidth + this.gutter) * 1
35074             });
35075             
35076             return pos;
35077         }
35078         
35079         var rand = Math.floor(Math.random() * (4 - box[0].y));
35080         
35081         pos.push({
35082             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35083             y : minY + (this.unitWidth + this.gutter) * rand
35084         });
35085         
35086         return pos;
35087         
35088     },
35089     
35090     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35091     {
35092         var pos = [];
35093         
35094         if(box[0].size == 'xs'){
35095             
35096             pos.push({
35097                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35098                 y : minY
35099             });
35100
35101             pos.push({
35102                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35103                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35104             });
35105             
35106             return pos;
35107             
35108         }
35109         
35110         pos.push({
35111             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35112             y : minY
35113         });
35114
35115         pos.push({
35116             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35117             y : minY + (this.unitWidth + this.gutter) * 2
35118         });
35119         
35120         return pos;
35121         
35122     },
35123     
35124     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35125     {
35126         var pos = [];
35127         
35128         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35129             
35130             pos.push({
35131                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35132                 y : minY
35133             });
35134
35135             pos.push({
35136                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35137                 y : minY + (this.unitWidth + this.gutter) * 1
35138             });
35139             
35140             pos.push({
35141                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35142                 y : minY + (this.unitWidth + this.gutter) * 2
35143             });
35144             
35145             return pos;
35146             
35147         }
35148         
35149         if(box[0].size == 'xs' && box[1].size == 'xs'){
35150             
35151             pos.push({
35152                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35153                 y : minY
35154             });
35155
35156             pos.push({
35157                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35158                 y : minY
35159             });
35160             
35161             pos.push({
35162                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35163                 y : minY + (this.unitWidth + this.gutter) * 1
35164             });
35165             
35166             return pos;
35167             
35168         }
35169         
35170         pos.push({
35171             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35172             y : minY
35173         });
35174
35175         pos.push({
35176             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35177             y : minY + (this.unitWidth + this.gutter) * 2
35178         });
35179
35180         pos.push({
35181             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35182             y : minY + (this.unitWidth + this.gutter) * 2
35183         });
35184             
35185         return pos;
35186         
35187     },
35188     
35189     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35190     {
35191         var pos = [];
35192         
35193         if(box[0].size == 'xs'){
35194             
35195             pos.push({
35196                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35197                 y : minY
35198             });
35199
35200             pos.push({
35201                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].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) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35207                 y : minY
35208             });
35209             
35210             pos.push({
35211                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35212                 y : minY + (this.unitWidth + this.gutter) * 1
35213             });
35214             
35215             return pos;
35216             
35217         }
35218         
35219         pos.push({
35220             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35221             y : minY
35222         });
35223         
35224         pos.push({
35225             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35226             y : minY + (this.unitWidth + this.gutter) * 2
35227         });
35228         
35229         pos.push({
35230             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].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) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35236             y : minY + (this.unitWidth + this.gutter) * 2
35237         });
35238
35239         return pos;
35240         
35241     },
35242     
35243     /**
35244     * remove a Masonry Brick
35245     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35246     */
35247     removeBrick : function(brick_id)
35248     {
35249         if (!brick_id) {
35250             return;
35251         }
35252         
35253         for (var i = 0; i<this.bricks.length; i++) {
35254             if (this.bricks[i].id == brick_id) {
35255                 this.bricks.splice(i,1);
35256                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35257                 this.initial();
35258             }
35259         }
35260     },
35261     
35262     /**
35263     * adds a Masonry Brick
35264     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35265     */
35266     addBrick : function(cfg)
35267     {
35268         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35269         //this.register(cn);
35270         cn.parentId = this.id;
35271         cn.render(this.el);
35272         return cn;
35273     },
35274     
35275     /**
35276     * register a Masonry Brick
35277     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35278     */
35279     
35280     register : function(brick)
35281     {
35282         this.bricks.push(brick);
35283         brick.masonryId = this.id;
35284     },
35285     
35286     /**
35287     * clear all the Masonry Brick
35288     */
35289     clearAll : function()
35290     {
35291         this.bricks = [];
35292         //this.getChildContainer().dom.innerHTML = "";
35293         this.el.dom.innerHTML = '';
35294     },
35295     
35296     getSelected : function()
35297     {
35298         if (!this.selectedBrick) {
35299             return false;
35300         }
35301         
35302         return this.selectedBrick;
35303     }
35304 });
35305
35306 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35307     
35308     groups: {},
35309      /**
35310     * register a Masonry Layout
35311     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35312     */
35313     
35314     register : function(layout)
35315     {
35316         this.groups[layout.id] = layout;
35317     },
35318     /**
35319     * fetch a  Masonry Layout based on the masonry layout ID
35320     * @param {string} the masonry layout to add
35321     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35322     */
35323     
35324     get: function(layout_id) {
35325         if (typeof(this.groups[layout_id]) == 'undefined') {
35326             return false;
35327         }
35328         return this.groups[layout_id] ;
35329     }
35330     
35331     
35332     
35333 });
35334
35335  
35336
35337  /**
35338  *
35339  * This is based on 
35340  * http://masonry.desandro.com
35341  *
35342  * The idea is to render all the bricks based on vertical width...
35343  *
35344  * The original code extends 'outlayer' - we might need to use that....
35345  * 
35346  */
35347
35348
35349 /**
35350  * @class Roo.bootstrap.LayoutMasonryAuto
35351  * @extends Roo.bootstrap.Component
35352  * Bootstrap Layout Masonry class
35353  * 
35354  * @constructor
35355  * Create a new Element
35356  * @param {Object} config The config object
35357  */
35358
35359 Roo.bootstrap.LayoutMasonryAuto = function(config){
35360     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35361 };
35362
35363 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35364     
35365       /**
35366      * @cfg {Boolean} isFitWidth  - resize the width..
35367      */   
35368     isFitWidth : false,  // options..
35369     /**
35370      * @cfg {Boolean} isOriginLeft = left align?
35371      */   
35372     isOriginLeft : true,
35373     /**
35374      * @cfg {Boolean} isOriginTop = top align?
35375      */   
35376     isOriginTop : false,
35377     /**
35378      * @cfg {Boolean} isLayoutInstant = no animation?
35379      */   
35380     isLayoutInstant : false, // needed?
35381     /**
35382      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35383      */   
35384     isResizingContainer : true,
35385     /**
35386      * @cfg {Number} columnWidth  width of the columns 
35387      */   
35388     
35389     columnWidth : 0,
35390     
35391     /**
35392      * @cfg {Number} maxCols maximum number of columns
35393      */   
35394     
35395     maxCols: 0,
35396     /**
35397      * @cfg {Number} padHeight padding below box..
35398      */   
35399     
35400     padHeight : 10, 
35401     
35402     /**
35403      * @cfg {Boolean} isAutoInitial defalut true
35404      */   
35405     
35406     isAutoInitial : true, 
35407     
35408     // private?
35409     gutter : 0,
35410     
35411     containerWidth: 0,
35412     initialColumnWidth : 0,
35413     currentSize : null,
35414     
35415     colYs : null, // array.
35416     maxY : 0,
35417     padWidth: 10,
35418     
35419     
35420     tag: 'div',
35421     cls: '',
35422     bricks: null, //CompositeElement
35423     cols : 0, // array?
35424     // element : null, // wrapped now this.el
35425     _isLayoutInited : null, 
35426     
35427     
35428     getAutoCreate : function(){
35429         
35430         var cfg = {
35431             tag: this.tag,
35432             cls: 'blog-masonary-wrapper ' + this.cls,
35433             cn : {
35434                 cls : 'mas-boxes masonary'
35435             }
35436         };
35437         
35438         return cfg;
35439     },
35440     
35441     getChildContainer: function( )
35442     {
35443         if (this.boxesEl) {
35444             return this.boxesEl;
35445         }
35446         
35447         this.boxesEl = this.el.select('.mas-boxes').first();
35448         
35449         return this.boxesEl;
35450     },
35451     
35452     
35453     initEvents : function()
35454     {
35455         var _this = this;
35456         
35457         if(this.isAutoInitial){
35458             Roo.log('hook children rendered');
35459             this.on('childrenrendered', function() {
35460                 Roo.log('children rendered');
35461                 _this.initial();
35462             } ,this);
35463         }
35464         
35465     },
35466     
35467     initial : function()
35468     {
35469         this.reloadItems();
35470
35471         this.currentSize = this.el.getBox(true);
35472
35473         /// was window resize... - let's see if this works..
35474         Roo.EventManager.onWindowResize(this.resize, this); 
35475
35476         if(!this.isAutoInitial){
35477             this.layout();
35478             return;
35479         }
35480         
35481         this.layout.defer(500,this);
35482     },
35483     
35484     reloadItems: function()
35485     {
35486         this.bricks = this.el.select('.masonry-brick', true);
35487         
35488         this.bricks.each(function(b) {
35489             //Roo.log(b.getSize());
35490             if (!b.attr('originalwidth')) {
35491                 b.attr('originalwidth',  b.getSize().width);
35492             }
35493             
35494         });
35495         
35496         Roo.log(this.bricks.elements.length);
35497     },
35498     
35499     resize : function()
35500     {
35501         Roo.log('resize');
35502         var cs = this.el.getBox(true);
35503         
35504         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35505             Roo.log("no change in with or X");
35506             return;
35507         }
35508         this.currentSize = cs;
35509         this.layout();
35510     },
35511     
35512     layout : function()
35513     {
35514          Roo.log('layout');
35515         this._resetLayout();
35516         //this._manageStamps();
35517       
35518         // don't animate first layout
35519         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35520         this.layoutItems( isInstant );
35521       
35522         // flag for initalized
35523         this._isLayoutInited = true;
35524     },
35525     
35526     layoutItems : function( isInstant )
35527     {
35528         //var items = this._getItemsForLayout( this.items );
35529         // original code supports filtering layout items.. we just ignore it..
35530         
35531         this._layoutItems( this.bricks , isInstant );
35532       
35533         this._postLayout();
35534     },
35535     _layoutItems : function ( items , isInstant)
35536     {
35537        //this.fireEvent( 'layout', this, items );
35538     
35539
35540         if ( !items || !items.elements.length ) {
35541           // no items, emit event with empty array
35542             return;
35543         }
35544
35545         var queue = [];
35546         items.each(function(item) {
35547             Roo.log("layout item");
35548             Roo.log(item);
35549             // get x/y object from method
35550             var position = this._getItemLayoutPosition( item );
35551             // enqueue
35552             position.item = item;
35553             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35554             queue.push( position );
35555         }, this);
35556       
35557         this._processLayoutQueue( queue );
35558     },
35559     /** Sets position of item in DOM
35560     * @param {Element} item
35561     * @param {Number} x - horizontal position
35562     * @param {Number} y - vertical position
35563     * @param {Boolean} isInstant - disables transitions
35564     */
35565     _processLayoutQueue : function( queue )
35566     {
35567         for ( var i=0, len = queue.length; i < len; i++ ) {
35568             var obj = queue[i];
35569             obj.item.position('absolute');
35570             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35571         }
35572     },
35573       
35574     
35575     /**
35576     * Any logic you want to do after each layout,
35577     * i.e. size the container
35578     */
35579     _postLayout : function()
35580     {
35581         this.resizeContainer();
35582     },
35583     
35584     resizeContainer : function()
35585     {
35586         if ( !this.isResizingContainer ) {
35587             return;
35588         }
35589         var size = this._getContainerSize();
35590         if ( size ) {
35591             this.el.setSize(size.width,size.height);
35592             this.boxesEl.setSize(size.width,size.height);
35593         }
35594     },
35595     
35596     
35597     
35598     _resetLayout : function()
35599     {
35600         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35601         this.colWidth = this.el.getWidth();
35602         //this.gutter = this.el.getWidth(); 
35603         
35604         this.measureColumns();
35605
35606         // reset column Y
35607         var i = this.cols;
35608         this.colYs = [];
35609         while (i--) {
35610             this.colYs.push( 0 );
35611         }
35612     
35613         this.maxY = 0;
35614     },
35615
35616     measureColumns : function()
35617     {
35618         this.getContainerWidth();
35619       // if columnWidth is 0, default to outerWidth of first item
35620         if ( !this.columnWidth ) {
35621             var firstItem = this.bricks.first();
35622             Roo.log(firstItem);
35623             this.columnWidth  = this.containerWidth;
35624             if (firstItem && firstItem.attr('originalwidth') ) {
35625                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35626             }
35627             // columnWidth fall back to item of first element
35628             Roo.log("set column width?");
35629                         this.initialColumnWidth = this.columnWidth  ;
35630
35631             // if first elem has no width, default to size of container
35632             
35633         }
35634         
35635         
35636         if (this.initialColumnWidth) {
35637             this.columnWidth = this.initialColumnWidth;
35638         }
35639         
35640         
35641             
35642         // column width is fixed at the top - however if container width get's smaller we should
35643         // reduce it...
35644         
35645         // this bit calcs how man columns..
35646             
35647         var columnWidth = this.columnWidth += this.gutter;
35648       
35649         // calculate columns
35650         var containerWidth = this.containerWidth + this.gutter;
35651         
35652         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35653         // fix rounding errors, typically with gutters
35654         var excess = columnWidth - containerWidth % columnWidth;
35655         
35656         
35657         // if overshoot is less than a pixel, round up, otherwise floor it
35658         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35659         cols = Math[ mathMethod ]( cols );
35660         this.cols = Math.max( cols, 1 );
35661         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35662         
35663          // padding positioning..
35664         var totalColWidth = this.cols * this.columnWidth;
35665         var padavail = this.containerWidth - totalColWidth;
35666         // so for 2 columns - we need 3 'pads'
35667         
35668         var padNeeded = (1+this.cols) * this.padWidth;
35669         
35670         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35671         
35672         this.columnWidth += padExtra
35673         //this.padWidth = Math.floor(padavail /  ( this.cols));
35674         
35675         // adjust colum width so that padding is fixed??
35676         
35677         // we have 3 columns ... total = width * 3
35678         // we have X left over... that should be used by 
35679         
35680         //if (this.expandC) {
35681             
35682         //}
35683         
35684         
35685         
35686     },
35687     
35688     getContainerWidth : function()
35689     {
35690        /* // container is parent if fit width
35691         var container = this.isFitWidth ? this.element.parentNode : this.element;
35692         // check that this.size and size are there
35693         // IE8 triggers resize on body size change, so they might not be
35694         
35695         var size = getSize( container );  //FIXME
35696         this.containerWidth = size && size.innerWidth; //FIXME
35697         */
35698          
35699         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35700         
35701     },
35702     
35703     _getItemLayoutPosition : function( item )  // what is item?
35704     {
35705         // we resize the item to our columnWidth..
35706       
35707         item.setWidth(this.columnWidth);
35708         item.autoBoxAdjust  = false;
35709         
35710         var sz = item.getSize();
35711  
35712         // how many columns does this brick span
35713         var remainder = this.containerWidth % this.columnWidth;
35714         
35715         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35716         // round if off by 1 pixel, otherwise use ceil
35717         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35718         colSpan = Math.min( colSpan, this.cols );
35719         
35720         // normally this should be '1' as we dont' currently allow multi width columns..
35721         
35722         var colGroup = this._getColGroup( colSpan );
35723         // get the minimum Y value from the columns
35724         var minimumY = Math.min.apply( Math, colGroup );
35725         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35726         
35727         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35728          
35729         // position the brick
35730         var position = {
35731             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35732             y: this.currentSize.y + minimumY + this.padHeight
35733         };
35734         
35735         Roo.log(position);
35736         // apply setHeight to necessary columns
35737         var setHeight = minimumY + sz.height + this.padHeight;
35738         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35739         
35740         var setSpan = this.cols + 1 - colGroup.length;
35741         for ( var i = 0; i < setSpan; i++ ) {
35742           this.colYs[ shortColIndex + i ] = setHeight ;
35743         }
35744       
35745         return position;
35746     },
35747     
35748     /**
35749      * @param {Number} colSpan - number of columns the element spans
35750      * @returns {Array} colGroup
35751      */
35752     _getColGroup : function( colSpan )
35753     {
35754         if ( colSpan < 2 ) {
35755           // if brick spans only one column, use all the column Ys
35756           return this.colYs;
35757         }
35758       
35759         var colGroup = [];
35760         // how many different places could this brick fit horizontally
35761         var groupCount = this.cols + 1 - colSpan;
35762         // for each group potential horizontal position
35763         for ( var i = 0; i < groupCount; i++ ) {
35764           // make an array of colY values for that one group
35765           var groupColYs = this.colYs.slice( i, i + colSpan );
35766           // and get the max value of the array
35767           colGroup[i] = Math.max.apply( Math, groupColYs );
35768         }
35769         return colGroup;
35770     },
35771     /*
35772     _manageStamp : function( stamp )
35773     {
35774         var stampSize =  stamp.getSize();
35775         var offset = stamp.getBox();
35776         // get the columns that this stamp affects
35777         var firstX = this.isOriginLeft ? offset.x : offset.right;
35778         var lastX = firstX + stampSize.width;
35779         var firstCol = Math.floor( firstX / this.columnWidth );
35780         firstCol = Math.max( 0, firstCol );
35781         
35782         var lastCol = Math.floor( lastX / this.columnWidth );
35783         // lastCol should not go over if multiple of columnWidth #425
35784         lastCol -= lastX % this.columnWidth ? 0 : 1;
35785         lastCol = Math.min( this.cols - 1, lastCol );
35786         
35787         // set colYs to bottom of the stamp
35788         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35789             stampSize.height;
35790             
35791         for ( var i = firstCol; i <= lastCol; i++ ) {
35792           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35793         }
35794     },
35795     */
35796     
35797     _getContainerSize : function()
35798     {
35799         this.maxY = Math.max.apply( Math, this.colYs );
35800         var size = {
35801             height: this.maxY
35802         };
35803       
35804         if ( this.isFitWidth ) {
35805             size.width = this._getContainerFitWidth();
35806         }
35807       
35808         return size;
35809     },
35810     
35811     _getContainerFitWidth : function()
35812     {
35813         var unusedCols = 0;
35814         // count unused columns
35815         var i = this.cols;
35816         while ( --i ) {
35817           if ( this.colYs[i] !== 0 ) {
35818             break;
35819           }
35820           unusedCols++;
35821         }
35822         // fit container to columns that have been used
35823         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35824     },
35825     
35826     needsResizeLayout : function()
35827     {
35828         var previousWidth = this.containerWidth;
35829         this.getContainerWidth();
35830         return previousWidth !== this.containerWidth;
35831     }
35832  
35833 });
35834
35835  
35836
35837  /*
35838  * - LGPL
35839  *
35840  * element
35841  * 
35842  */
35843
35844 /**
35845  * @class Roo.bootstrap.MasonryBrick
35846  * @extends Roo.bootstrap.Component
35847  * Bootstrap MasonryBrick class
35848  * 
35849  * @constructor
35850  * Create a new MasonryBrick
35851  * @param {Object} config The config object
35852  */
35853
35854 Roo.bootstrap.MasonryBrick = function(config){
35855     
35856     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35857     
35858     Roo.bootstrap.MasonryBrick.register(this);
35859     
35860     this.addEvents({
35861         // raw events
35862         /**
35863          * @event click
35864          * When a MasonryBrick is clcik
35865          * @param {Roo.bootstrap.MasonryBrick} this
35866          * @param {Roo.EventObject} e
35867          */
35868         "click" : true
35869     });
35870 };
35871
35872 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35873     
35874     /**
35875      * @cfg {String} title
35876      */   
35877     title : '',
35878     /**
35879      * @cfg {String} html
35880      */   
35881     html : '',
35882     /**
35883      * @cfg {String} bgimage
35884      */   
35885     bgimage : '',
35886     /**
35887      * @cfg {String} videourl
35888      */   
35889     videourl : '',
35890     /**
35891      * @cfg {String} cls
35892      */   
35893     cls : '',
35894     /**
35895      * @cfg {String} href
35896      */   
35897     href : '',
35898     /**
35899      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35900      */   
35901     size : 'xs',
35902     
35903     /**
35904      * @cfg {String} placetitle (center|bottom)
35905      */   
35906     placetitle : '',
35907     
35908     /**
35909      * @cfg {Boolean} isFitContainer defalut true
35910      */   
35911     isFitContainer : true, 
35912     
35913     /**
35914      * @cfg {Boolean} preventDefault defalut false
35915      */   
35916     preventDefault : false, 
35917     
35918     /**
35919      * @cfg {Boolean} inverse defalut false
35920      */   
35921     maskInverse : false, 
35922     
35923     getAutoCreate : function()
35924     {
35925         if(!this.isFitContainer){
35926             return this.getSplitAutoCreate();
35927         }
35928         
35929         var cls = 'masonry-brick masonry-brick-full';
35930         
35931         if(this.href.length){
35932             cls += ' masonry-brick-link';
35933         }
35934         
35935         if(this.bgimage.length){
35936             cls += ' masonry-brick-image';
35937         }
35938         
35939         if(this.maskInverse){
35940             cls += ' mask-inverse';
35941         }
35942         
35943         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35944             cls += ' enable-mask';
35945         }
35946         
35947         if(this.size){
35948             cls += ' masonry-' + this.size + '-brick';
35949         }
35950         
35951         if(this.placetitle.length){
35952             
35953             switch (this.placetitle) {
35954                 case 'center' :
35955                     cls += ' masonry-center-title';
35956                     break;
35957                 case 'bottom' :
35958                     cls += ' masonry-bottom-title';
35959                     break;
35960                 default:
35961                     break;
35962             }
35963             
35964         } else {
35965             if(!this.html.length && !this.bgimage.length){
35966                 cls += ' masonry-center-title';
35967             }
35968
35969             if(!this.html.length && this.bgimage.length){
35970                 cls += ' masonry-bottom-title';
35971             }
35972         }
35973         
35974         if(this.cls){
35975             cls += ' ' + this.cls;
35976         }
35977         
35978         var cfg = {
35979             tag: (this.href.length) ? 'a' : 'div',
35980             cls: cls,
35981             cn: [
35982                 {
35983                     tag: 'div',
35984                     cls: 'masonry-brick-mask'
35985                 },
35986                 {
35987                     tag: 'div',
35988                     cls: 'masonry-brick-paragraph',
35989                     cn: []
35990                 }
35991             ]
35992         };
35993         
35994         if(this.href.length){
35995             cfg.href = this.href;
35996         }
35997         
35998         var cn = cfg.cn[1].cn;
35999         
36000         if(this.title.length){
36001             cn.push({
36002                 tag: 'h4',
36003                 cls: 'masonry-brick-title',
36004                 html: this.title
36005             });
36006         }
36007         
36008         if(this.html.length){
36009             cn.push({
36010                 tag: 'p',
36011                 cls: 'masonry-brick-text',
36012                 html: this.html
36013             });
36014         }
36015         
36016         if (!this.title.length && !this.html.length) {
36017             cfg.cn[1].cls += ' hide';
36018         }
36019         
36020         if(this.bgimage.length){
36021             cfg.cn.push({
36022                 tag: 'img',
36023                 cls: 'masonry-brick-image-view',
36024                 src: this.bgimage
36025             });
36026         }
36027         
36028         if(this.videourl.length){
36029             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36030             // youtube support only?
36031             cfg.cn.push({
36032                 tag: 'iframe',
36033                 cls: 'masonry-brick-image-view',
36034                 src: vurl,
36035                 frameborder : 0,
36036                 allowfullscreen : true
36037             });
36038         }
36039         
36040         return cfg;
36041         
36042     },
36043     
36044     getSplitAutoCreate : function()
36045     {
36046         var cls = 'masonry-brick masonry-brick-split';
36047         
36048         if(this.href.length){
36049             cls += ' masonry-brick-link';
36050         }
36051         
36052         if(this.bgimage.length){
36053             cls += ' masonry-brick-image';
36054         }
36055         
36056         if(this.size){
36057             cls += ' masonry-' + this.size + '-brick';
36058         }
36059         
36060         switch (this.placetitle) {
36061             case 'center' :
36062                 cls += ' masonry-center-title';
36063                 break;
36064             case 'bottom' :
36065                 cls += ' masonry-bottom-title';
36066                 break;
36067             default:
36068                 if(!this.bgimage.length){
36069                     cls += ' masonry-center-title';
36070                 }
36071
36072                 if(this.bgimage.length){
36073                     cls += ' masonry-bottom-title';
36074                 }
36075                 break;
36076         }
36077         
36078         if(this.cls){
36079             cls += ' ' + this.cls;
36080         }
36081         
36082         var cfg = {
36083             tag: (this.href.length) ? 'a' : 'div',
36084             cls: cls,
36085             cn: [
36086                 {
36087                     tag: 'div',
36088                     cls: 'masonry-brick-split-head',
36089                     cn: [
36090                         {
36091                             tag: 'div',
36092                             cls: 'masonry-brick-paragraph',
36093                             cn: []
36094                         }
36095                     ]
36096                 },
36097                 {
36098                     tag: 'div',
36099                     cls: 'masonry-brick-split-body',
36100                     cn: []
36101                 }
36102             ]
36103         };
36104         
36105         if(this.href.length){
36106             cfg.href = this.href;
36107         }
36108         
36109         if(this.title.length){
36110             cfg.cn[0].cn[0].cn.push({
36111                 tag: 'h4',
36112                 cls: 'masonry-brick-title',
36113                 html: this.title
36114             });
36115         }
36116         
36117         if(this.html.length){
36118             cfg.cn[1].cn.push({
36119                 tag: 'p',
36120                 cls: 'masonry-brick-text',
36121                 html: this.html
36122             });
36123         }
36124
36125         if(this.bgimage.length){
36126             cfg.cn[0].cn.push({
36127                 tag: 'img',
36128                 cls: 'masonry-brick-image-view',
36129                 src: this.bgimage
36130             });
36131         }
36132         
36133         if(this.videourl.length){
36134             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36135             // youtube support only?
36136             cfg.cn[0].cn.cn.push({
36137                 tag: 'iframe',
36138                 cls: 'masonry-brick-image-view',
36139                 src: vurl,
36140                 frameborder : 0,
36141                 allowfullscreen : true
36142             });
36143         }
36144         
36145         return cfg;
36146     },
36147     
36148     initEvents: function() 
36149     {
36150         switch (this.size) {
36151             case 'xs' :
36152                 this.x = 1;
36153                 this.y = 1;
36154                 break;
36155             case 'sm' :
36156                 this.x = 2;
36157                 this.y = 2;
36158                 break;
36159             case 'md' :
36160             case 'md-left' :
36161             case 'md-right' :
36162                 this.x = 3;
36163                 this.y = 3;
36164                 break;
36165             case 'tall' :
36166                 this.x = 2;
36167                 this.y = 3;
36168                 break;
36169             case 'wide' :
36170                 this.x = 3;
36171                 this.y = 2;
36172                 break;
36173             case 'wide-thin' :
36174                 this.x = 3;
36175                 this.y = 1;
36176                 break;
36177                         
36178             default :
36179                 break;
36180         }
36181         
36182         if(Roo.isTouch){
36183             this.el.on('touchstart', this.onTouchStart, this);
36184             this.el.on('touchmove', this.onTouchMove, this);
36185             this.el.on('touchend', this.onTouchEnd, this);
36186             this.el.on('contextmenu', this.onContextMenu, this);
36187         } else {
36188             this.el.on('mouseenter'  ,this.enter, this);
36189             this.el.on('mouseleave', this.leave, this);
36190             this.el.on('click', this.onClick, this);
36191         }
36192         
36193         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36194             this.parent().bricks.push(this);   
36195         }
36196         
36197     },
36198     
36199     onClick: function(e, el)
36200     {
36201         var time = this.endTimer - this.startTimer;
36202         // Roo.log(e.preventDefault());
36203         if(Roo.isTouch){
36204             if(time > 1000){
36205                 e.preventDefault();
36206                 return;
36207             }
36208         }
36209         
36210         if(!this.preventDefault){
36211             return;
36212         }
36213         
36214         e.preventDefault();
36215         
36216         if (this.activeClass != '') {
36217             this.selectBrick();
36218         }
36219         
36220         this.fireEvent('click', this, e);
36221     },
36222     
36223     enter: function(e, el)
36224     {
36225         e.preventDefault();
36226         
36227         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36228             return;
36229         }
36230         
36231         if(this.bgimage.length && this.html.length){
36232             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36233         }
36234     },
36235     
36236     leave: function(e, el)
36237     {
36238         e.preventDefault();
36239         
36240         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36241             return;
36242         }
36243         
36244         if(this.bgimage.length && this.html.length){
36245             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36246         }
36247     },
36248     
36249     onTouchStart: function(e, el)
36250     {
36251 //        e.preventDefault();
36252         
36253         this.touchmoved = false;
36254         
36255         if(!this.isFitContainer){
36256             return;
36257         }
36258         
36259         if(!this.bgimage.length || !this.html.length){
36260             return;
36261         }
36262         
36263         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36264         
36265         this.timer = new Date().getTime();
36266         
36267     },
36268     
36269     onTouchMove: function(e, el)
36270     {
36271         this.touchmoved = true;
36272     },
36273     
36274     onContextMenu : function(e,el)
36275     {
36276         e.preventDefault();
36277         e.stopPropagation();
36278         return false;
36279     },
36280     
36281     onTouchEnd: function(e, el)
36282     {
36283 //        e.preventDefault();
36284         
36285         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36286         
36287             this.leave(e,el);
36288             
36289             return;
36290         }
36291         
36292         if(!this.bgimage.length || !this.html.length){
36293             
36294             if(this.href.length){
36295                 window.location.href = this.href;
36296             }
36297             
36298             return;
36299         }
36300         
36301         if(!this.isFitContainer){
36302             return;
36303         }
36304         
36305         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36306         
36307         window.location.href = this.href;
36308     },
36309     
36310     //selection on single brick only
36311     selectBrick : function() {
36312         
36313         if (!this.parentId) {
36314             return;
36315         }
36316         
36317         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36318         var index = m.selectedBrick.indexOf(this.id);
36319         
36320         if ( index > -1) {
36321             m.selectedBrick.splice(index,1);
36322             this.el.removeClass(this.activeClass);
36323             return;
36324         }
36325         
36326         for(var i = 0; i < m.selectedBrick.length; i++) {
36327             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36328             b.el.removeClass(b.activeClass);
36329         }
36330         
36331         m.selectedBrick = [];
36332         
36333         m.selectedBrick.push(this.id);
36334         this.el.addClass(this.activeClass);
36335         return;
36336     },
36337     
36338     isSelected : function(){
36339         return this.el.hasClass(this.activeClass);
36340         
36341     }
36342 });
36343
36344 Roo.apply(Roo.bootstrap.MasonryBrick, {
36345     
36346     //groups: {},
36347     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36348      /**
36349     * register a Masonry Brick
36350     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36351     */
36352     
36353     register : function(brick)
36354     {
36355         //this.groups[brick.id] = brick;
36356         this.groups.add(brick.id, brick);
36357     },
36358     /**
36359     * fetch a  masonry brick based on the masonry brick ID
36360     * @param {string} the masonry brick to add
36361     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36362     */
36363     
36364     get: function(brick_id) 
36365     {
36366         // if (typeof(this.groups[brick_id]) == 'undefined') {
36367         //     return false;
36368         // }
36369         // return this.groups[brick_id] ;
36370         
36371         if(this.groups.key(brick_id)) {
36372             return this.groups.key(brick_id);
36373         }
36374         
36375         return false;
36376     }
36377     
36378     
36379     
36380 });
36381
36382  /*
36383  * - LGPL
36384  *
36385  * element
36386  * 
36387  */
36388
36389 /**
36390  * @class Roo.bootstrap.Brick
36391  * @extends Roo.bootstrap.Component
36392  * Bootstrap Brick class
36393  * 
36394  * @constructor
36395  * Create a new Brick
36396  * @param {Object} config The config object
36397  */
36398
36399 Roo.bootstrap.Brick = function(config){
36400     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36401     
36402     this.addEvents({
36403         // raw events
36404         /**
36405          * @event click
36406          * When a Brick is click
36407          * @param {Roo.bootstrap.Brick} this
36408          * @param {Roo.EventObject} e
36409          */
36410         "click" : true
36411     });
36412 };
36413
36414 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36415     
36416     /**
36417      * @cfg {String} title
36418      */   
36419     title : '',
36420     /**
36421      * @cfg {String} html
36422      */   
36423     html : '',
36424     /**
36425      * @cfg {String} bgimage
36426      */   
36427     bgimage : '',
36428     /**
36429      * @cfg {String} cls
36430      */   
36431     cls : '',
36432     /**
36433      * @cfg {String} href
36434      */   
36435     href : '',
36436     /**
36437      * @cfg {String} video
36438      */   
36439     video : '',
36440     /**
36441      * @cfg {Boolean} square
36442      */   
36443     square : true,
36444     
36445     getAutoCreate : function()
36446     {
36447         var cls = 'roo-brick';
36448         
36449         if(this.href.length){
36450             cls += ' roo-brick-link';
36451         }
36452         
36453         if(this.bgimage.length){
36454             cls += ' roo-brick-image';
36455         }
36456         
36457         if(!this.html.length && !this.bgimage.length){
36458             cls += ' roo-brick-center-title';
36459         }
36460         
36461         if(!this.html.length && this.bgimage.length){
36462             cls += ' roo-brick-bottom-title';
36463         }
36464         
36465         if(this.cls){
36466             cls += ' ' + this.cls;
36467         }
36468         
36469         var cfg = {
36470             tag: (this.href.length) ? 'a' : 'div',
36471             cls: cls,
36472             cn: [
36473                 {
36474                     tag: 'div',
36475                     cls: 'roo-brick-paragraph',
36476                     cn: []
36477                 }
36478             ]
36479         };
36480         
36481         if(this.href.length){
36482             cfg.href = this.href;
36483         }
36484         
36485         var cn = cfg.cn[0].cn;
36486         
36487         if(this.title.length){
36488             cn.push({
36489                 tag: 'h4',
36490                 cls: 'roo-brick-title',
36491                 html: this.title
36492             });
36493         }
36494         
36495         if(this.html.length){
36496             cn.push({
36497                 tag: 'p',
36498                 cls: 'roo-brick-text',
36499                 html: this.html
36500             });
36501         } else {
36502             cn.cls += ' hide';
36503         }
36504         
36505         if(this.bgimage.length){
36506             cfg.cn.push({
36507                 tag: 'img',
36508                 cls: 'roo-brick-image-view',
36509                 src: this.bgimage
36510             });
36511         }
36512         
36513         return cfg;
36514     },
36515     
36516     initEvents: function() 
36517     {
36518         if(this.title.length || this.html.length){
36519             this.el.on('mouseenter'  ,this.enter, this);
36520             this.el.on('mouseleave', this.leave, this);
36521         }
36522         
36523         Roo.EventManager.onWindowResize(this.resize, this); 
36524         
36525         if(this.bgimage.length){
36526             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36527             this.imageEl.on('load', this.onImageLoad, this);
36528             return;
36529         }
36530         
36531         this.resize();
36532     },
36533     
36534     onImageLoad : function()
36535     {
36536         this.resize();
36537     },
36538     
36539     resize : function()
36540     {
36541         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36542         
36543         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36544         
36545         if(this.bgimage.length){
36546             var image = this.el.select('.roo-brick-image-view', true).first();
36547             
36548             image.setWidth(paragraph.getWidth());
36549             
36550             if(this.square){
36551                 image.setHeight(paragraph.getWidth());
36552             }
36553             
36554             this.el.setHeight(image.getHeight());
36555             paragraph.setHeight(image.getHeight());
36556             
36557         }
36558         
36559     },
36560     
36561     enter: function(e, el)
36562     {
36563         e.preventDefault();
36564         
36565         if(this.bgimage.length){
36566             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36567             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36568         }
36569     },
36570     
36571     leave: function(e, el)
36572     {
36573         e.preventDefault();
36574         
36575         if(this.bgimage.length){
36576             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36577             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36578         }
36579     }
36580     
36581 });
36582
36583  
36584
36585  /*
36586  * - LGPL
36587  *
36588  * Number field 
36589  */
36590
36591 /**
36592  * @class Roo.bootstrap.form.NumberField
36593  * @extends Roo.bootstrap.form.Input
36594  * Bootstrap NumberField class
36595  * 
36596  * 
36597  * 
36598  * 
36599  * @constructor
36600  * Create a new NumberField
36601  * @param {Object} config The config object
36602  */
36603
36604 Roo.bootstrap.form.NumberField = function(config){
36605     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36606 };
36607
36608 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36609     
36610     /**
36611      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36612      */
36613     allowDecimals : true,
36614     /**
36615      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36616      */
36617     decimalSeparator : ".",
36618     /**
36619      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36620      */
36621     decimalPrecision : 2,
36622     /**
36623      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36624      */
36625     allowNegative : true,
36626     
36627     /**
36628      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36629      */
36630     allowZero: true,
36631     /**
36632      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36633      */
36634     minValue : Number.NEGATIVE_INFINITY,
36635     /**
36636      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36637      */
36638     maxValue : Number.MAX_VALUE,
36639     /**
36640      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36641      */
36642     minText : "The minimum value for this field is {0}",
36643     /**
36644      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36645      */
36646     maxText : "The maximum value for this field is {0}",
36647     /**
36648      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36649      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36650      */
36651     nanText : "{0} is not a valid number",
36652     /**
36653      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36654      */
36655     thousandsDelimiter : false,
36656     /**
36657      * @cfg {String} valueAlign alignment of value
36658      */
36659     valueAlign : "left",
36660
36661     getAutoCreate : function()
36662     {
36663         var hiddenInput = {
36664             tag: 'input',
36665             type: 'hidden',
36666             id: Roo.id(),
36667             cls: 'hidden-number-input'
36668         };
36669         
36670         if (this.name) {
36671             hiddenInput.name = this.name;
36672         }
36673         
36674         this.name = '';
36675         
36676         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36677         
36678         this.name = hiddenInput.name;
36679         
36680         if(cfg.cn.length > 0) {
36681             cfg.cn.push(hiddenInput);
36682         }
36683         
36684         return cfg;
36685     },
36686
36687     // private
36688     initEvents : function()
36689     {   
36690         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36691         
36692         var allowed = "0123456789";
36693         
36694         if(this.allowDecimals){
36695             allowed += this.decimalSeparator;
36696         }
36697         
36698         if(this.allowNegative){
36699             allowed += "-";
36700         }
36701         
36702         if(this.thousandsDelimiter) {
36703             allowed += ",";
36704         }
36705         
36706         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36707         
36708         var keyPress = function(e){
36709             
36710             var k = e.getKey();
36711             
36712             var c = e.getCharCode();
36713             
36714             if(
36715                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36716                     allowed.indexOf(String.fromCharCode(c)) === -1
36717             ){
36718                 e.stopEvent();
36719                 return;
36720             }
36721             
36722             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36723                 return;
36724             }
36725             
36726             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36727                 e.stopEvent();
36728             }
36729         };
36730         
36731         this.el.on("keypress", keyPress, this);
36732     },
36733     
36734     validateValue : function(value)
36735     {
36736         
36737         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36738             return false;
36739         }
36740         
36741         var num = this.parseValue(value);
36742         
36743         if(isNaN(num)){
36744             this.markInvalid(String.format(this.nanText, value));
36745             return false;
36746         }
36747         
36748         if(num < this.minValue){
36749             this.markInvalid(String.format(this.minText, this.minValue));
36750             return false;
36751         }
36752         
36753         if(num > this.maxValue){
36754             this.markInvalid(String.format(this.maxText, this.maxValue));
36755             return false;
36756         }
36757         
36758         return true;
36759     },
36760
36761     getValue : function()
36762     {
36763         var v = this.hiddenEl().getValue();
36764         
36765         return this.fixPrecision(this.parseValue(v));
36766     },
36767
36768     parseValue : function(value)
36769     {
36770         if(this.thousandsDelimiter) {
36771             value += "";
36772             r = new RegExp(",", "g");
36773             value = value.replace(r, "");
36774         }
36775         
36776         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36777         return isNaN(value) ? '' : value;
36778     },
36779
36780     fixPrecision : function(value)
36781     {
36782         if(this.thousandsDelimiter) {
36783             value += "";
36784             r = new RegExp(",", "g");
36785             value = value.replace(r, "");
36786         }
36787         
36788         var nan = isNaN(value);
36789         
36790         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36791             return nan ? '' : value;
36792         }
36793         return parseFloat(value).toFixed(this.decimalPrecision);
36794     },
36795
36796     setValue : function(v)
36797     {
36798         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36799         
36800         this.value = v;
36801         
36802         if(this.rendered){
36803             
36804             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36805             
36806             this.inputEl().dom.value = (v == '') ? '' :
36807                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36808             
36809             if(!this.allowZero && v === '0') {
36810                 this.hiddenEl().dom.value = '';
36811                 this.inputEl().dom.value = '';
36812             }
36813             
36814             this.validate();
36815         }
36816     },
36817
36818     decimalPrecisionFcn : function(v)
36819     {
36820         return Math.floor(v);
36821     },
36822
36823     beforeBlur : function()
36824     {
36825         var v = this.parseValue(this.getRawValue());
36826         
36827         if(v || v === 0 || v === ''){
36828             this.setValue(v);
36829         }
36830     },
36831     
36832     hiddenEl : function()
36833     {
36834         return this.el.select('input.hidden-number-input',true).first();
36835     }
36836     
36837 });
36838
36839  
36840
36841 /*
36842 * Licence: LGPL
36843 */
36844
36845 /**
36846  * @class Roo.bootstrap.DocumentSlider
36847  * @extends Roo.bootstrap.Component
36848  * Bootstrap DocumentSlider class
36849  * 
36850  * @constructor
36851  * Create a new DocumentViewer
36852  * @param {Object} config The config object
36853  */
36854
36855 Roo.bootstrap.DocumentSlider = function(config){
36856     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36857     
36858     this.files = [];
36859     
36860     this.addEvents({
36861         /**
36862          * @event initial
36863          * Fire after initEvent
36864          * @param {Roo.bootstrap.DocumentSlider} this
36865          */
36866         "initial" : true,
36867         /**
36868          * @event update
36869          * Fire after update
36870          * @param {Roo.bootstrap.DocumentSlider} this
36871          */
36872         "update" : true,
36873         /**
36874          * @event click
36875          * Fire after click
36876          * @param {Roo.bootstrap.DocumentSlider} this
36877          */
36878         "click" : true
36879     });
36880 };
36881
36882 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36883     
36884     files : false,
36885     
36886     indicator : 0,
36887     
36888     getAutoCreate : function()
36889     {
36890         var cfg = {
36891             tag : 'div',
36892             cls : 'roo-document-slider',
36893             cn : [
36894                 {
36895                     tag : 'div',
36896                     cls : 'roo-document-slider-header',
36897                     cn : [
36898                         {
36899                             tag : 'div',
36900                             cls : 'roo-document-slider-header-title'
36901                         }
36902                     ]
36903                 },
36904                 {
36905                     tag : 'div',
36906                     cls : 'roo-document-slider-body',
36907                     cn : [
36908                         {
36909                             tag : 'div',
36910                             cls : 'roo-document-slider-prev',
36911                             cn : [
36912                                 {
36913                                     tag : 'i',
36914                                     cls : 'fa fa-chevron-left'
36915                                 }
36916                             ]
36917                         },
36918                         {
36919                             tag : 'div',
36920                             cls : 'roo-document-slider-thumb',
36921                             cn : [
36922                                 {
36923                                     tag : 'img',
36924                                     cls : 'roo-document-slider-image'
36925                                 }
36926                             ]
36927                         },
36928                         {
36929                             tag : 'div',
36930                             cls : 'roo-document-slider-next',
36931                             cn : [
36932                                 {
36933                                     tag : 'i',
36934                                     cls : 'fa fa-chevron-right'
36935                                 }
36936                             ]
36937                         }
36938                     ]
36939                 }
36940             ]
36941         };
36942         
36943         return cfg;
36944     },
36945     
36946     initEvents : function()
36947     {
36948         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36949         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36950         
36951         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36952         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36953         
36954         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36955         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36956         
36957         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36958         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36959         
36960         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36961         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36962         
36963         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36964         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36965         
36966         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36967         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36968         
36969         this.thumbEl.on('click', this.onClick, this);
36970         
36971         this.prevIndicator.on('click', this.prev, this);
36972         
36973         this.nextIndicator.on('click', this.next, this);
36974         
36975     },
36976     
36977     initial : function()
36978     {
36979         if(this.files.length){
36980             this.indicator = 1;
36981             this.update()
36982         }
36983         
36984         this.fireEvent('initial', this);
36985     },
36986     
36987     update : function()
36988     {
36989         this.imageEl.attr('src', this.files[this.indicator - 1]);
36990         
36991         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36992         
36993         this.prevIndicator.show();
36994         
36995         if(this.indicator == 1){
36996             this.prevIndicator.hide();
36997         }
36998         
36999         this.nextIndicator.show();
37000         
37001         if(this.indicator == this.files.length){
37002             this.nextIndicator.hide();
37003         }
37004         
37005         this.thumbEl.scrollTo('top');
37006         
37007         this.fireEvent('update', this);
37008     },
37009     
37010     onClick : function(e)
37011     {
37012         e.preventDefault();
37013         
37014         this.fireEvent('click', this);
37015     },
37016     
37017     prev : function(e)
37018     {
37019         e.preventDefault();
37020         
37021         this.indicator = Math.max(1, this.indicator - 1);
37022         
37023         this.update();
37024     },
37025     
37026     next : function(e)
37027     {
37028         e.preventDefault();
37029         
37030         this.indicator = Math.min(this.files.length, this.indicator + 1);
37031         
37032         this.update();
37033     }
37034 });
37035 /*
37036  * - LGPL
37037  *
37038  * RadioSet
37039  *
37040  *
37041  */
37042
37043 /**
37044  * @class Roo.bootstrap.form.RadioSet
37045  * @extends Roo.bootstrap.form.Input
37046  * @children Roo.bootstrap.form.Radio
37047  * Bootstrap RadioSet class
37048  * @cfg {String} indicatorpos (left|right) default left
37049  * @cfg {Boolean} inline (true|false) inline the element (default true)
37050  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37051  * @constructor
37052  * Create a new RadioSet
37053  * @param {Object} config The config object
37054  */
37055
37056 Roo.bootstrap.form.RadioSet = function(config){
37057     
37058     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37059     
37060     this.radioes = [];
37061     
37062     Roo.bootstrap.form.RadioSet.register(this);
37063     
37064     this.addEvents({
37065         /**
37066         * @event check
37067         * Fires when the element is checked or unchecked.
37068         * @param {Roo.bootstrap.form.RadioSet} this This radio
37069         * @param {Roo.bootstrap.form.Radio} item The checked item
37070         */
37071        check : true,
37072        /**
37073         * @event click
37074         * Fires when the element is click.
37075         * @param {Roo.bootstrap.form.RadioSet} this This radio set
37076         * @param {Roo.bootstrap.form.Radio} item The checked item
37077         * @param {Roo.EventObject} e The event object
37078         */
37079        click : true
37080     });
37081     
37082 };
37083
37084 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
37085
37086     radioes : false,
37087     
37088     inline : true,
37089     
37090     weight : '',
37091     
37092     indicatorpos : 'left',
37093     
37094     getAutoCreate : function()
37095     {
37096         var label = {
37097             tag : 'label',
37098             cls : 'roo-radio-set-label',
37099             cn : [
37100                 {
37101                     tag : 'span',
37102                     html : this.fieldLabel
37103                 }
37104             ]
37105         };
37106         if (Roo.bootstrap.version == 3) {
37107             
37108             
37109             if(this.indicatorpos == 'left'){
37110                 label.cn.unshift({
37111                     tag : 'i',
37112                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37113                     tooltip : 'This field is required'
37114                 });
37115             } else {
37116                 label.cn.push({
37117                     tag : 'i',
37118                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37119                     tooltip : 'This field is required'
37120                 });
37121             }
37122         }
37123         var items = {
37124             tag : 'div',
37125             cls : 'roo-radio-set-items'
37126         };
37127         
37128         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37129         
37130         if (align === 'left' && this.fieldLabel.length) {
37131             
37132             items = {
37133                 cls : "roo-radio-set-right", 
37134                 cn: [
37135                     items
37136                 ]
37137             };
37138             
37139             if(this.labelWidth > 12){
37140                 label.style = "width: " + this.labelWidth + 'px';
37141             }
37142             
37143             if(this.labelWidth < 13 && this.labelmd == 0){
37144                 this.labelmd = this.labelWidth;
37145             }
37146             
37147             if(this.labellg > 0){
37148                 label.cls += ' col-lg-' + this.labellg;
37149                 items.cls += ' col-lg-' + (12 - this.labellg);
37150             }
37151             
37152             if(this.labelmd > 0){
37153                 label.cls += ' col-md-' + this.labelmd;
37154                 items.cls += ' col-md-' + (12 - this.labelmd);
37155             }
37156             
37157             if(this.labelsm > 0){
37158                 label.cls += ' col-sm-' + this.labelsm;
37159                 items.cls += ' col-sm-' + (12 - this.labelsm);
37160             }
37161             
37162             if(this.labelxs > 0){
37163                 label.cls += ' col-xs-' + this.labelxs;
37164                 items.cls += ' col-xs-' + (12 - this.labelxs);
37165             }
37166         }
37167         
37168         var cfg = {
37169             tag : 'div',
37170             cls : 'roo-radio-set',
37171             cn : [
37172                 {
37173                     tag : 'input',
37174                     cls : 'roo-radio-set-input',
37175                     type : 'hidden',
37176                     name : this.name,
37177                     value : this.value ? this.value :  ''
37178                 },
37179                 label,
37180                 items
37181             ]
37182         };
37183         
37184         if(this.weight.length){
37185             cfg.cls += ' roo-radio-' + this.weight;
37186         }
37187         
37188         if(this.inline) {
37189             cfg.cls += ' roo-radio-set-inline';
37190         }
37191         
37192         var settings=this;
37193         ['xs','sm','md','lg'].map(function(size){
37194             if (settings[size]) {
37195                 cfg.cls += ' col-' + size + '-' + settings[size];
37196             }
37197         });
37198         
37199         return cfg;
37200         
37201     },
37202
37203     initEvents : function()
37204     {
37205         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37206         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37207         
37208         if(!this.fieldLabel.length){
37209             this.labelEl.hide();
37210         }
37211         
37212         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37213         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37214         
37215         this.indicator = this.indicatorEl();
37216         
37217         if(this.indicator){
37218             this.indicator.addClass('invisible');
37219         }
37220         
37221         this.originalValue = this.getValue();
37222         
37223     },
37224     
37225     inputEl: function ()
37226     {
37227         return this.el.select('.roo-radio-set-input', true).first();
37228     },
37229     
37230     getChildContainer : function()
37231     {
37232         return this.itemsEl;
37233     },
37234     
37235     register : function(item)
37236     {
37237         this.radioes.push(item);
37238         
37239     },
37240     
37241     validate : function()
37242     {   
37243         if(this.getVisibilityEl().hasClass('hidden')){
37244             return true;
37245         }
37246         
37247         var valid = false;
37248         
37249         Roo.each(this.radioes, function(i){
37250             if(!i.checked){
37251                 return;
37252             }
37253             
37254             valid = true;
37255             return false;
37256         });
37257         
37258         if(this.allowBlank) {
37259             return true;
37260         }
37261         
37262         if(this.disabled || valid){
37263             this.markValid();
37264             return true;
37265         }
37266         
37267         this.markInvalid();
37268         return false;
37269         
37270     },
37271     
37272     markValid : function()
37273     {
37274         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37275             this.indicatorEl().removeClass('visible');
37276             this.indicatorEl().addClass('invisible');
37277         }
37278         
37279         
37280         if (Roo.bootstrap.version == 3) {
37281             this.el.removeClass([this.invalidClass, this.validClass]);
37282             this.el.addClass(this.validClass);
37283         } else {
37284             this.el.removeClass(['is-invalid','is-valid']);
37285             this.el.addClass(['is-valid']);
37286         }
37287         this.fireEvent('valid', this);
37288     },
37289     
37290     markInvalid : function(msg)
37291     {
37292         if(this.allowBlank || this.disabled){
37293             return;
37294         }
37295         
37296         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37297             this.indicatorEl().removeClass('invisible');
37298             this.indicatorEl().addClass('visible');
37299         }
37300         if (Roo.bootstrap.version == 3) {
37301             this.el.removeClass([this.invalidClass, this.validClass]);
37302             this.el.addClass(this.invalidClass);
37303         } else {
37304             this.el.removeClass(['is-invalid','is-valid']);
37305             this.el.addClass(['is-invalid']);
37306         }
37307         
37308         this.fireEvent('invalid', this, msg);
37309         
37310     },
37311     
37312     setValue : function(v, suppressEvent)
37313     {   
37314         if(this.value === v){
37315             return;
37316         }
37317         
37318         this.value = v;
37319         
37320         if(this.rendered){
37321             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37322         }
37323         
37324         Roo.each(this.radioes, function(i){
37325             i.checked = false;
37326             i.el.removeClass('checked');
37327         });
37328         
37329         Roo.each(this.radioes, function(i){
37330             
37331             if(i.value === v || i.value.toString() === v.toString()){
37332                 i.checked = true;
37333                 i.el.addClass('checked');
37334                 
37335                 if(suppressEvent !== true){
37336                     this.fireEvent('check', this, i);
37337                 }
37338                 
37339                 return false;
37340             }
37341             
37342         }, this);
37343         
37344         this.validate();
37345     },
37346     
37347     clearInvalid : function(){
37348         
37349         if(!this.el || this.preventMark){
37350             return;
37351         }
37352         
37353         this.el.removeClass([this.invalidClass]);
37354         
37355         this.fireEvent('valid', this);
37356     }
37357     
37358 });
37359
37360 Roo.apply(Roo.bootstrap.form.RadioSet, {
37361     
37362     groups: {},
37363     
37364     register : function(set)
37365     {
37366         this.groups[set.name] = set;
37367     },
37368     
37369     get: function(name) 
37370     {
37371         if (typeof(this.groups[name]) == 'undefined') {
37372             return false;
37373         }
37374         
37375         return this.groups[name] ;
37376     }
37377     
37378 });
37379 /*
37380  * Based on:
37381  * Ext JS Library 1.1.1
37382  * Copyright(c) 2006-2007, Ext JS, LLC.
37383  *
37384  * Originally Released Under LGPL - original licence link has changed is not relivant.
37385  *
37386  * Fork - LGPL
37387  * <script type="text/javascript">
37388  */
37389
37390
37391 /**
37392  * @class Roo.bootstrap.SplitBar
37393  * @extends Roo.util.Observable
37394  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37395  * <br><br>
37396  * Usage:
37397  * <pre><code>
37398 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37399                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37400 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37401 split.minSize = 100;
37402 split.maxSize = 600;
37403 split.animate = true;
37404 split.on('moved', splitterMoved);
37405 </code></pre>
37406  * @constructor
37407  * Create a new SplitBar
37408  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37409  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37410  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37411  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37412                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37413                         position of the SplitBar).
37414  */
37415 Roo.bootstrap.SplitBar = function(cfg){
37416     
37417     /** @private */
37418     
37419     //{
37420     //  dragElement : elm
37421     //  resizingElement: el,
37422         // optional..
37423     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37424     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37425         // existingProxy ???
37426     //}
37427     
37428     this.el = Roo.get(cfg.dragElement, true);
37429     this.el.dom.unselectable = "on";
37430     /** @private */
37431     this.resizingEl = Roo.get(cfg.resizingElement, true);
37432
37433     /**
37434      * @private
37435      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37436      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37437      * @type Number
37438      */
37439     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37440     
37441     /**
37442      * The minimum size of the resizing element. (Defaults to 0)
37443      * @type Number
37444      */
37445     this.minSize = 0;
37446     
37447     /**
37448      * The maximum size of the resizing element. (Defaults to 2000)
37449      * @type Number
37450      */
37451     this.maxSize = 2000;
37452     
37453     /**
37454      * Whether to animate the transition to the new size
37455      * @type Boolean
37456      */
37457     this.animate = false;
37458     
37459     /**
37460      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37461      * @type Boolean
37462      */
37463     this.useShim = false;
37464     
37465     /** @private */
37466     this.shim = null;
37467     
37468     if(!cfg.existingProxy){
37469         /** @private */
37470         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37471     }else{
37472         this.proxy = Roo.get(cfg.existingProxy).dom;
37473     }
37474     /** @private */
37475     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37476     
37477     /** @private */
37478     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37479     
37480     /** @private */
37481     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37482     
37483     /** @private */
37484     this.dragSpecs = {};
37485     
37486     /**
37487      * @private The adapter to use to positon and resize elements
37488      */
37489     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37490     this.adapter.init(this);
37491     
37492     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37493         /** @private */
37494         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37495         this.el.addClass("roo-splitbar-h");
37496     }else{
37497         /** @private */
37498         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37499         this.el.addClass("roo-splitbar-v");
37500     }
37501     
37502     this.addEvents({
37503         /**
37504          * @event resize
37505          * Fires when the splitter is moved (alias for {@link #event-moved})
37506          * @param {Roo.bootstrap.SplitBar} this
37507          * @param {Number} newSize the new width or height
37508          */
37509         "resize" : true,
37510         /**
37511          * @event moved
37512          * Fires when the splitter is moved
37513          * @param {Roo.bootstrap.SplitBar} this
37514          * @param {Number} newSize the new width or height
37515          */
37516         "moved" : true,
37517         /**
37518          * @event beforeresize
37519          * Fires before the splitter is dragged
37520          * @param {Roo.bootstrap.SplitBar} this
37521          */
37522         "beforeresize" : true,
37523
37524         "beforeapply" : true
37525     });
37526
37527     Roo.util.Observable.call(this);
37528 };
37529
37530 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37531     onStartProxyDrag : function(x, y){
37532         this.fireEvent("beforeresize", this);
37533         if(!this.overlay){
37534             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37535             o.unselectable();
37536             o.enableDisplayMode("block");
37537             // all splitbars share the same overlay
37538             Roo.bootstrap.SplitBar.prototype.overlay = o;
37539         }
37540         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37541         this.overlay.show();
37542         Roo.get(this.proxy).setDisplayed("block");
37543         var size = this.adapter.getElementSize(this);
37544         this.activeMinSize = this.getMinimumSize();;
37545         this.activeMaxSize = this.getMaximumSize();;
37546         var c1 = size - this.activeMinSize;
37547         var c2 = Math.max(this.activeMaxSize - size, 0);
37548         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37549             this.dd.resetConstraints();
37550             this.dd.setXConstraint(
37551                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37552                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37553             );
37554             this.dd.setYConstraint(0, 0);
37555         }else{
37556             this.dd.resetConstraints();
37557             this.dd.setXConstraint(0, 0);
37558             this.dd.setYConstraint(
37559                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37560                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37561             );
37562          }
37563         this.dragSpecs.startSize = size;
37564         this.dragSpecs.startPoint = [x, y];
37565         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37566     },
37567     
37568     /** 
37569      * @private Called after the drag operation by the DDProxy
37570      */
37571     onEndProxyDrag : function(e){
37572         Roo.get(this.proxy).setDisplayed(false);
37573         var endPoint = Roo.lib.Event.getXY(e);
37574         if(this.overlay){
37575             this.overlay.hide();
37576         }
37577         var newSize;
37578         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37579             newSize = this.dragSpecs.startSize + 
37580                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37581                     endPoint[0] - this.dragSpecs.startPoint[0] :
37582                     this.dragSpecs.startPoint[0] - endPoint[0]
37583                 );
37584         }else{
37585             newSize = this.dragSpecs.startSize + 
37586                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37587                     endPoint[1] - this.dragSpecs.startPoint[1] :
37588                     this.dragSpecs.startPoint[1] - endPoint[1]
37589                 );
37590         }
37591         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37592         if(newSize != this.dragSpecs.startSize){
37593             if(this.fireEvent('beforeapply', this, newSize) !== false){
37594                 this.adapter.setElementSize(this, newSize);
37595                 this.fireEvent("moved", this, newSize);
37596                 this.fireEvent("resize", this, newSize);
37597             }
37598         }
37599     },
37600     
37601     /**
37602      * Get the adapter this SplitBar uses
37603      * @return The adapter object
37604      */
37605     getAdapter : function(){
37606         return this.adapter;
37607     },
37608     
37609     /**
37610      * Set the adapter this SplitBar uses
37611      * @param {Object} adapter A SplitBar adapter object
37612      */
37613     setAdapter : function(adapter){
37614         this.adapter = adapter;
37615         this.adapter.init(this);
37616     },
37617     
37618     /**
37619      * Gets the minimum size for the resizing element
37620      * @return {Number} The minimum size
37621      */
37622     getMinimumSize : function(){
37623         return this.minSize;
37624     },
37625     
37626     /**
37627      * Sets the minimum size for the resizing element
37628      * @param {Number} minSize The minimum size
37629      */
37630     setMinimumSize : function(minSize){
37631         this.minSize = minSize;
37632     },
37633     
37634     /**
37635      * Gets the maximum size for the resizing element
37636      * @return {Number} The maximum size
37637      */
37638     getMaximumSize : function(){
37639         return this.maxSize;
37640     },
37641     
37642     /**
37643      * Sets the maximum size for the resizing element
37644      * @param {Number} maxSize The maximum size
37645      */
37646     setMaximumSize : function(maxSize){
37647         this.maxSize = maxSize;
37648     },
37649     
37650     /**
37651      * Sets the initialize size for the resizing element
37652      * @param {Number} size The initial size
37653      */
37654     setCurrentSize : function(size){
37655         var oldAnimate = this.animate;
37656         this.animate = false;
37657         this.adapter.setElementSize(this, size);
37658         this.animate = oldAnimate;
37659     },
37660     
37661     /**
37662      * Destroy this splitbar. 
37663      * @param {Boolean} removeEl True to remove the element
37664      */
37665     destroy : function(removeEl){
37666         if(this.shim){
37667             this.shim.remove();
37668         }
37669         this.dd.unreg();
37670         this.proxy.parentNode.removeChild(this.proxy);
37671         if(removeEl){
37672             this.el.remove();
37673         }
37674     }
37675 });
37676
37677 /**
37678  * @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.
37679  */
37680 Roo.bootstrap.SplitBar.createProxy = function(dir){
37681     var proxy = new Roo.Element(document.createElement("div"));
37682     proxy.unselectable();
37683     var cls = 'roo-splitbar-proxy';
37684     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37685     document.body.appendChild(proxy.dom);
37686     return proxy.dom;
37687 };
37688
37689 /** 
37690  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37691  * Default Adapter. It assumes the splitter and resizing element are not positioned
37692  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37693  */
37694 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37695 };
37696
37697 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37698     // do nothing for now
37699     init : function(s){
37700     
37701     },
37702     /**
37703      * Called before drag operations to get the current size of the resizing element. 
37704      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37705      */
37706      getElementSize : function(s){
37707         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37708             return s.resizingEl.getWidth();
37709         }else{
37710             return s.resizingEl.getHeight();
37711         }
37712     },
37713     
37714     /**
37715      * Called after drag operations to set the size of the resizing element.
37716      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37717      * @param {Number} newSize The new size to set
37718      * @param {Function} onComplete A function to be invoked when resizing is complete
37719      */
37720     setElementSize : function(s, newSize, onComplete){
37721         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37722             if(!s.animate){
37723                 s.resizingEl.setWidth(newSize);
37724                 if(onComplete){
37725                     onComplete(s, newSize);
37726                 }
37727             }else{
37728                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37729             }
37730         }else{
37731             
37732             if(!s.animate){
37733                 s.resizingEl.setHeight(newSize);
37734                 if(onComplete){
37735                     onComplete(s, newSize);
37736                 }
37737             }else{
37738                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37739             }
37740         }
37741     }
37742 };
37743
37744 /** 
37745  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37746  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37747  * Adapter that  moves the splitter element to align with the resized sizing element. 
37748  * Used with an absolute positioned SplitBar.
37749  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37750  * document.body, make sure you assign an id to the body element.
37751  */
37752 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37753     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37754     this.container = Roo.get(container);
37755 };
37756
37757 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37758     init : function(s){
37759         this.basic.init(s);
37760     },
37761     
37762     getElementSize : function(s){
37763         return this.basic.getElementSize(s);
37764     },
37765     
37766     setElementSize : function(s, newSize, onComplete){
37767         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37768     },
37769     
37770     moveSplitter : function(s){
37771         var yes = Roo.bootstrap.SplitBar;
37772         switch(s.placement){
37773             case yes.LEFT:
37774                 s.el.setX(s.resizingEl.getRight());
37775                 break;
37776             case yes.RIGHT:
37777                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37778                 break;
37779             case yes.TOP:
37780                 s.el.setY(s.resizingEl.getBottom());
37781                 break;
37782             case yes.BOTTOM:
37783                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37784                 break;
37785         }
37786     }
37787 };
37788
37789 /**
37790  * Orientation constant - Create a vertical SplitBar
37791  * @static
37792  * @type Number
37793  */
37794 Roo.bootstrap.SplitBar.VERTICAL = 1;
37795
37796 /**
37797  * Orientation constant - Create a horizontal SplitBar
37798  * @static
37799  * @type Number
37800  */
37801 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37802
37803 /**
37804  * Placement constant - The resizing element is to the left of the splitter element
37805  * @static
37806  * @type Number
37807  */
37808 Roo.bootstrap.SplitBar.LEFT = 1;
37809
37810 /**
37811  * Placement constant - The resizing element is to the right of the splitter element
37812  * @static
37813  * @type Number
37814  */
37815 Roo.bootstrap.SplitBar.RIGHT = 2;
37816
37817 /**
37818  * Placement constant - The resizing element is positioned above the splitter element
37819  * @static
37820  * @type Number
37821  */
37822 Roo.bootstrap.SplitBar.TOP = 3;
37823
37824 /**
37825  * Placement constant - The resizing element is positioned under splitter element
37826  * @static
37827  * @type Number
37828  */
37829 Roo.bootstrap.SplitBar.BOTTOM = 4;
37830 /*
37831  * Based on:
37832  * Ext JS Library 1.1.1
37833  * Copyright(c) 2006-2007, Ext JS, LLC.
37834  *
37835  * Originally Released Under LGPL - original licence link has changed is not relivant.
37836  *
37837  * Fork - LGPL
37838  * <script type="text/javascript">
37839  */
37840
37841 /**
37842  * @class Roo.bootstrap.layout.Manager
37843  * @extends Roo.bootstrap.Component
37844  * @abstract
37845  * Base class for layout managers.
37846  */
37847 Roo.bootstrap.layout.Manager = function(config)
37848 {
37849     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37850
37851
37852
37853
37854
37855     /** false to disable window resize monitoring @type Boolean */
37856     this.monitorWindowResize = true;
37857     this.regions = {};
37858     this.addEvents({
37859         /**
37860          * @event layout
37861          * Fires when a layout is performed.
37862          * @param {Roo.LayoutManager} this
37863          */
37864         "layout" : true,
37865         /**
37866          * @event regionresized
37867          * Fires when the user resizes a region.
37868          * @param {Roo.LayoutRegion} region The resized region
37869          * @param {Number} newSize The new size (width for east/west, height for north/south)
37870          */
37871         "regionresized" : true,
37872         /**
37873          * @event regioncollapsed
37874          * Fires when a region is collapsed.
37875          * @param {Roo.LayoutRegion} region The collapsed region
37876          */
37877         "regioncollapsed" : true,
37878         /**
37879          * @event regionexpanded
37880          * Fires when a region is expanded.
37881          * @param {Roo.LayoutRegion} region The expanded region
37882          */
37883         "regionexpanded" : true
37884     });
37885     this.updating = false;
37886
37887     if (config.el) {
37888         this.el = Roo.get(config.el);
37889         this.initEvents();
37890     }
37891
37892 };
37893
37894 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37895
37896
37897     regions : null,
37898
37899     monitorWindowResize : true,
37900
37901
37902     updating : false,
37903
37904
37905     onRender : function(ct, position)
37906     {
37907         if(!this.el){
37908             this.el = Roo.get(ct);
37909             this.initEvents();
37910         }
37911         //this.fireEvent('render',this);
37912     },
37913
37914
37915     initEvents: function()
37916     {
37917
37918
37919         // ie scrollbar fix
37920         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37921             document.body.scroll = "no";
37922         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37923             this.el.position('relative');
37924         }
37925         this.id = this.el.id;
37926         this.el.addClass("roo-layout-container");
37927         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37928         if(this.el.dom != document.body ) {
37929             this.el.on('resize', this.layout,this);
37930             this.el.on('show', this.layout,this);
37931         }
37932
37933     },
37934
37935     /**
37936      * Returns true if this layout is currently being updated
37937      * @return {Boolean}
37938      */
37939     isUpdating : function(){
37940         return this.updating;
37941     },
37942
37943     /**
37944      * Suspend the LayoutManager from doing auto-layouts while
37945      * making multiple add or remove calls
37946      */
37947     beginUpdate : function(){
37948         this.updating = true;
37949     },
37950
37951     /**
37952      * Restore auto-layouts and optionally disable the manager from performing a layout
37953      * @param {Boolean} noLayout true to disable a layout update
37954      */
37955     endUpdate : function(noLayout){
37956         this.updating = false;
37957         if(!noLayout){
37958             this.layout();
37959         }
37960     },
37961
37962     layout: function(){
37963         // abstract...
37964     },
37965
37966     onRegionResized : function(region, newSize){
37967         this.fireEvent("regionresized", region, newSize);
37968         this.layout();
37969     },
37970
37971     onRegionCollapsed : function(region){
37972         this.fireEvent("regioncollapsed", region);
37973     },
37974
37975     onRegionExpanded : function(region){
37976         this.fireEvent("regionexpanded", region);
37977     },
37978
37979     /**
37980      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37981      * performs box-model adjustments.
37982      * @return {Object} The size as an object {width: (the width), height: (the height)}
37983      */
37984     getViewSize : function()
37985     {
37986         var size;
37987         if(this.el.dom != document.body){
37988             size = this.el.getSize();
37989         }else{
37990             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37991         }
37992         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37993         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37994         return size;
37995     },
37996
37997     /**
37998      * Returns the Element this layout is bound to.
37999      * @return {Roo.Element}
38000      */
38001     getEl : function(){
38002         return this.el;
38003     },
38004
38005     /**
38006      * Returns the specified region.
38007      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38008      * @return {Roo.LayoutRegion}
38009      */
38010     getRegion : function(target){
38011         return this.regions[target.toLowerCase()];
38012     },
38013
38014     onWindowResize : function(){
38015         if(this.monitorWindowResize){
38016             this.layout();
38017         }
38018     }
38019 });
38020 /*
38021  * Based on:
38022  * Ext JS Library 1.1.1
38023  * Copyright(c) 2006-2007, Ext JS, LLC.
38024  *
38025  * Originally Released Under LGPL - original licence link has changed is not relivant.
38026  *
38027  * Fork - LGPL
38028  * <script type="text/javascript">
38029  */
38030 /**
38031  * @class Roo.bootstrap.layout.Border
38032  * @extends Roo.bootstrap.layout.Manager
38033  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38034  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
38035  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38036  * please see: examples/bootstrap/nested.html<br><br>
38037  
38038 <b>The container the layout is rendered into can be either the body element or any other element.
38039 If it is not the body element, the container needs to either be an absolute positioned element,
38040 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38041 the container size if it is not the body element.</b>
38042
38043 * @constructor
38044 * Create a new Border
38045 * @param {Object} config Configuration options
38046  */
38047 Roo.bootstrap.layout.Border = function(config){
38048     config = config || {};
38049     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38050     
38051     
38052     
38053     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38054         if(config[region]){
38055             config[region].region = region;
38056             this.addRegion(config[region]);
38057         }
38058     },this);
38059     
38060 };
38061
38062 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38063
38064 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38065     
38066         /**
38067          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38068          */
38069         /**
38070          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38071          */
38072         /**
38073          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38074          */
38075         /**
38076          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38077          */
38078         /**
38079          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38080          */
38081         
38082         
38083         
38084         
38085     parent : false, // this might point to a 'nest' or a ???
38086     
38087     /**
38088      * Creates and adds a new region if it doesn't already exist.
38089      * @param {String} target The target region key (north, south, east, west or center).
38090      * @param {Object} config The regions config object
38091      * @return {BorderLayoutRegion} The new region
38092      */
38093     addRegion : function(config)
38094     {
38095         if(!this.regions[config.region]){
38096             var r = this.factory(config);
38097             this.bindRegion(r);
38098         }
38099         return this.regions[config.region];
38100     },
38101
38102     // private (kinda)
38103     bindRegion : function(r){
38104         this.regions[r.config.region] = r;
38105         
38106         r.on("visibilitychange",    this.layout, this);
38107         r.on("paneladded",          this.layout, this);
38108         r.on("panelremoved",        this.layout, this);
38109         r.on("invalidated",         this.layout, this);
38110         r.on("resized",             this.onRegionResized, this);
38111         r.on("collapsed",           this.onRegionCollapsed, this);
38112         r.on("expanded",            this.onRegionExpanded, this);
38113     },
38114
38115     /**
38116      * Performs a layout update.
38117      */
38118     layout : function()
38119     {
38120         if(this.updating) {
38121             return;
38122         }
38123         
38124         // render all the rebions if they have not been done alreayd?
38125         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38126             if(this.regions[region] && !this.regions[region].bodyEl){
38127                 this.regions[region].onRender(this.el)
38128             }
38129         },this);
38130         
38131         var size = this.getViewSize();
38132         var w = size.width;
38133         var h = size.height;
38134         var centerW = w;
38135         var centerH = h;
38136         var centerY = 0;
38137         var centerX = 0;
38138         //var x = 0, y = 0;
38139
38140         var rs = this.regions;
38141         var north = rs["north"];
38142         var south = rs["south"]; 
38143         var west = rs["west"];
38144         var east = rs["east"];
38145         var center = rs["center"];
38146         //if(this.hideOnLayout){ // not supported anymore
38147             //c.el.setStyle("display", "none");
38148         //}
38149         if(north && north.isVisible()){
38150             var b = north.getBox();
38151             var m = north.getMargins();
38152             b.width = w - (m.left+m.right);
38153             b.x = m.left;
38154             b.y = m.top;
38155             centerY = b.height + b.y + m.bottom;
38156             centerH -= centerY;
38157             north.updateBox(this.safeBox(b));
38158         }
38159         if(south && south.isVisible()){
38160             var b = south.getBox();
38161             var m = south.getMargins();
38162             b.width = w - (m.left+m.right);
38163             b.x = m.left;
38164             var totalHeight = (b.height + m.top + m.bottom);
38165             b.y = h - totalHeight + m.top;
38166             centerH -= totalHeight;
38167             south.updateBox(this.safeBox(b));
38168         }
38169         if(west && west.isVisible()){
38170             var b = west.getBox();
38171             var m = west.getMargins();
38172             b.height = centerH - (m.top+m.bottom);
38173             b.x = m.left;
38174             b.y = centerY + m.top;
38175             var totalWidth = (b.width + m.left + m.right);
38176             centerX += totalWidth;
38177             centerW -= totalWidth;
38178             west.updateBox(this.safeBox(b));
38179         }
38180         if(east && east.isVisible()){
38181             var b = east.getBox();
38182             var m = east.getMargins();
38183             b.height = centerH - (m.top+m.bottom);
38184             var totalWidth = (b.width + m.left + m.right);
38185             b.x = w - totalWidth + m.left;
38186             b.y = centerY + m.top;
38187             centerW -= totalWidth;
38188             east.updateBox(this.safeBox(b));
38189         }
38190         if(center){
38191             var m = center.getMargins();
38192             var centerBox = {
38193                 x: centerX + m.left,
38194                 y: centerY + m.top,
38195                 width: centerW - (m.left+m.right),
38196                 height: centerH - (m.top+m.bottom)
38197             };
38198             //if(this.hideOnLayout){
38199                 //center.el.setStyle("display", "block");
38200             //}
38201             center.updateBox(this.safeBox(centerBox));
38202         }
38203         this.el.repaint();
38204         this.fireEvent("layout", this);
38205     },
38206
38207     // private
38208     safeBox : function(box){
38209         box.width = Math.max(0, box.width);
38210         box.height = Math.max(0, box.height);
38211         return box;
38212     },
38213
38214     /**
38215      * Adds a ContentPanel (or subclass) to this layout.
38216      * @param {String} target The target region key (north, south, east, west or center).
38217      * @param {Roo.ContentPanel} panel The panel to add
38218      * @return {Roo.ContentPanel} The added panel
38219      */
38220     add : function(target, panel){
38221          
38222         target = target.toLowerCase();
38223         return this.regions[target].add(panel);
38224     },
38225
38226     /**
38227      * Remove a ContentPanel (or subclass) to this layout.
38228      * @param {String} target The target region key (north, south, east, west or center).
38229      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38230      * @return {Roo.ContentPanel} The removed panel
38231      */
38232     remove : function(target, panel){
38233         target = target.toLowerCase();
38234         return this.regions[target].remove(panel);
38235     },
38236
38237     /**
38238      * Searches all regions for a panel with the specified id
38239      * @param {String} panelId
38240      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38241      */
38242     findPanel : function(panelId){
38243         var rs = this.regions;
38244         for(var target in rs){
38245             if(typeof rs[target] != "function"){
38246                 var p = rs[target].getPanel(panelId);
38247                 if(p){
38248                     return p;
38249                 }
38250             }
38251         }
38252         return null;
38253     },
38254
38255     /**
38256      * Searches all regions for a panel with the specified id and activates (shows) it.
38257      * @param {String/ContentPanel} panelId The panels id or the panel itself
38258      * @return {Roo.ContentPanel} The shown panel or null
38259      */
38260     showPanel : function(panelId) {
38261       var rs = this.regions;
38262       for(var target in rs){
38263          var r = rs[target];
38264          if(typeof r != "function"){
38265             if(r.hasPanel(panelId)){
38266                return r.showPanel(panelId);
38267             }
38268          }
38269       }
38270       return null;
38271    },
38272
38273    /**
38274      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38275      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38276      */
38277    /*
38278     restoreState : function(provider){
38279         if(!provider){
38280             provider = Roo.state.Manager;
38281         }
38282         var sm = new Roo.LayoutStateManager();
38283         sm.init(this, provider);
38284     },
38285 */
38286  
38287  
38288     /**
38289      * Adds a xtype elements to the layout.
38290      * <pre><code>
38291
38292 layout.addxtype({
38293        xtype : 'ContentPanel',
38294        region: 'west',
38295        items: [ .... ]
38296    }
38297 );
38298
38299 layout.addxtype({
38300         xtype : 'NestedLayoutPanel',
38301         region: 'west',
38302         layout: {
38303            center: { },
38304            west: { }   
38305         },
38306         items : [ ... list of content panels or nested layout panels.. ]
38307    }
38308 );
38309 </code></pre>
38310      * @param {Object} cfg Xtype definition of item to add.
38311      */
38312     addxtype : function(cfg)
38313     {
38314         // basically accepts a pannel...
38315         // can accept a layout region..!?!?
38316         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38317         
38318         
38319         // theory?  children can only be panels??
38320         
38321         //if (!cfg.xtype.match(/Panel$/)) {
38322         //    return false;
38323         //}
38324         var ret = false;
38325         
38326         if (typeof(cfg.region) == 'undefined') {
38327             Roo.log("Failed to add Panel, region was not set");
38328             Roo.log(cfg);
38329             return false;
38330         }
38331         var region = cfg.region;
38332         delete cfg.region;
38333         
38334           
38335         var xitems = [];
38336         if (cfg.items) {
38337             xitems = cfg.items;
38338             delete cfg.items;
38339         }
38340         var nb = false;
38341         
38342         if ( region == 'center') {
38343             Roo.log("Center: " + cfg.title);
38344         }
38345         
38346         
38347         switch(cfg.xtype) 
38348         {
38349             case 'Content':  // ContentPanel (el, cfg)
38350             case 'Scroll':  // ContentPanel (el, cfg)
38351             case 'View': 
38352                 cfg.autoCreate = cfg.autoCreate || true;
38353                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38354                 //} else {
38355                 //    var el = this.el.createChild();
38356                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38357                 //}
38358                 
38359                 this.add(region, ret);
38360                 break;
38361             
38362             /*
38363             case 'TreePanel': // our new panel!
38364                 cfg.el = this.el.createChild();
38365                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38366                 this.add(region, ret);
38367                 break;
38368             */
38369             
38370             case 'Nest': 
38371                 // create a new Layout (which is  a Border Layout...
38372                 
38373                 var clayout = cfg.layout;
38374                 clayout.el  = this.el.createChild();
38375                 clayout.items   = clayout.items  || [];
38376                 
38377                 delete cfg.layout;
38378                 
38379                 // replace this exitems with the clayout ones..
38380                 xitems = clayout.items;
38381                  
38382                 // force background off if it's in center...
38383                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38384                     cfg.background = false;
38385                 }
38386                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38387                 
38388                 
38389                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38390                 //console.log('adding nested layout panel '  + cfg.toSource());
38391                 this.add(region, ret);
38392                 nb = {}; /// find first...
38393                 break;
38394             
38395             case 'Grid':
38396                 
38397                 // needs grid and region
38398                 
38399                 //var el = this.getRegion(region).el.createChild();
38400                 /*
38401                  *var el = this.el.createChild();
38402                 // create the grid first...
38403                 cfg.grid.container = el;
38404                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38405                 */
38406                 
38407                 if (region == 'center' && this.active ) {
38408                     cfg.background = false;
38409                 }
38410                 
38411                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38412                 
38413                 this.add(region, ret);
38414                 /*
38415                 if (cfg.background) {
38416                     // render grid on panel activation (if panel background)
38417                     ret.on('activate', function(gp) {
38418                         if (!gp.grid.rendered) {
38419                     //        gp.grid.render(el);
38420                         }
38421                     });
38422                 } else {
38423                   //  cfg.grid.render(el);
38424                 }
38425                 */
38426                 break;
38427            
38428            
38429             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38430                 // it was the old xcomponent building that caused this before.
38431                 // espeically if border is the top element in the tree.
38432                 ret = this;
38433                 break; 
38434                 
38435                     
38436                 
38437                 
38438                 
38439             default:
38440                 /*
38441                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38442                     
38443                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38444                     this.add(region, ret);
38445                 } else {
38446                 */
38447                     Roo.log(cfg);
38448                     throw "Can not add '" + cfg.xtype + "' to Border";
38449                     return null;
38450              
38451                                 
38452              
38453         }
38454         this.beginUpdate();
38455         // add children..
38456         var region = '';
38457         var abn = {};
38458         Roo.each(xitems, function(i)  {
38459             region = nb && i.region ? i.region : false;
38460             
38461             var add = ret.addxtype(i);
38462            
38463             if (region) {
38464                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38465                 if (!i.background) {
38466                     abn[region] = nb[region] ;
38467                 }
38468             }
38469             
38470         });
38471         this.endUpdate();
38472
38473         // make the last non-background panel active..
38474         //if (nb) { Roo.log(abn); }
38475         if (nb) {
38476             
38477             for(var r in abn) {
38478                 region = this.getRegion(r);
38479                 if (region) {
38480                     // tried using nb[r], but it does not work..
38481                      
38482                     region.showPanel(abn[r]);
38483                    
38484                 }
38485             }
38486         }
38487         return ret;
38488         
38489     },
38490     
38491     
38492 // private
38493     factory : function(cfg)
38494     {
38495         
38496         var validRegions = Roo.bootstrap.layout.Border.regions;
38497
38498         var target = cfg.region;
38499         cfg.mgr = this;
38500         
38501         var r = Roo.bootstrap.layout;
38502         Roo.log(target);
38503         switch(target){
38504             case "north":
38505                 return new r.North(cfg);
38506             case "south":
38507                 return new r.South(cfg);
38508             case "east":
38509                 return new r.East(cfg);
38510             case "west":
38511                 return new r.West(cfg);
38512             case "center":
38513                 return new r.Center(cfg);
38514         }
38515         throw 'Layout region "'+target+'" not supported.';
38516     }
38517     
38518     
38519 });
38520  /*
38521  * Based on:
38522  * Ext JS Library 1.1.1
38523  * Copyright(c) 2006-2007, Ext JS, LLC.
38524  *
38525  * Originally Released Under LGPL - original licence link has changed is not relivant.
38526  *
38527  * Fork - LGPL
38528  * <script type="text/javascript">
38529  */
38530  
38531 /**
38532  * @class Roo.bootstrap.layout.Basic
38533  * @extends Roo.util.Observable
38534  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38535  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38536  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38537  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38538  * @cfg {string}   region  the region that it inhabits..
38539  * @cfg {bool}   skipConfig skip config?
38540  * 
38541
38542  */
38543 Roo.bootstrap.layout.Basic = function(config){
38544     
38545     this.mgr = config.mgr;
38546     
38547     this.position = config.region;
38548     
38549     var skipConfig = config.skipConfig;
38550     
38551     this.events = {
38552         /**
38553          * @scope Roo.BasicLayoutRegion
38554          */
38555         
38556         /**
38557          * @event beforeremove
38558          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38559          * @param {Roo.LayoutRegion} this
38560          * @param {Roo.ContentPanel} panel The panel
38561          * @param {Object} e The cancel event object
38562          */
38563         "beforeremove" : true,
38564         /**
38565          * @event invalidated
38566          * Fires when the layout for this region is changed.
38567          * @param {Roo.LayoutRegion} this
38568          */
38569         "invalidated" : true,
38570         /**
38571          * @event visibilitychange
38572          * Fires when this region is shown or hidden 
38573          * @param {Roo.LayoutRegion} this
38574          * @param {Boolean} visibility true or false
38575          */
38576         "visibilitychange" : true,
38577         /**
38578          * @event paneladded
38579          * Fires when a panel is added. 
38580          * @param {Roo.LayoutRegion} this
38581          * @param {Roo.ContentPanel} panel The panel
38582          */
38583         "paneladded" : true,
38584         /**
38585          * @event panelremoved
38586          * Fires when a panel is removed. 
38587          * @param {Roo.LayoutRegion} this
38588          * @param {Roo.ContentPanel} panel The panel
38589          */
38590         "panelremoved" : true,
38591         /**
38592          * @event beforecollapse
38593          * Fires when this region before collapse.
38594          * @param {Roo.LayoutRegion} this
38595          */
38596         "beforecollapse" : true,
38597         /**
38598          * @event collapsed
38599          * Fires when this region is collapsed.
38600          * @param {Roo.LayoutRegion} this
38601          */
38602         "collapsed" : true,
38603         /**
38604          * @event expanded
38605          * Fires when this region is expanded.
38606          * @param {Roo.LayoutRegion} this
38607          */
38608         "expanded" : true,
38609         /**
38610          * @event slideshow
38611          * Fires when this region is slid into view.
38612          * @param {Roo.LayoutRegion} this
38613          */
38614         "slideshow" : true,
38615         /**
38616          * @event slidehide
38617          * Fires when this region slides out of view. 
38618          * @param {Roo.LayoutRegion} this
38619          */
38620         "slidehide" : true,
38621         /**
38622          * @event panelactivated
38623          * Fires when a panel is activated. 
38624          * @param {Roo.LayoutRegion} this
38625          * @param {Roo.ContentPanel} panel The activated panel
38626          */
38627         "panelactivated" : true,
38628         /**
38629          * @event resized
38630          * Fires when the user resizes this region. 
38631          * @param {Roo.LayoutRegion} this
38632          * @param {Number} newSize The new size (width for east/west, height for north/south)
38633          */
38634         "resized" : true
38635     };
38636     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38637     this.panels = new Roo.util.MixedCollection();
38638     this.panels.getKey = this.getPanelId.createDelegate(this);
38639     this.box = null;
38640     this.activePanel = null;
38641     // ensure listeners are added...
38642     
38643     if (config.listeners || config.events) {
38644         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38645             listeners : config.listeners || {},
38646             events : config.events || {}
38647         });
38648     }
38649     
38650     if(skipConfig !== true){
38651         this.applyConfig(config);
38652     }
38653 };
38654
38655 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38656 {
38657     getPanelId : function(p){
38658         return p.getId();
38659     },
38660     
38661     applyConfig : function(config){
38662         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38663         this.config = config;
38664         
38665     },
38666     
38667     /**
38668      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38669      * the width, for horizontal (north, south) the height.
38670      * @param {Number} newSize The new width or height
38671      */
38672     resizeTo : function(newSize){
38673         var el = this.el ? this.el :
38674                  (this.activePanel ? this.activePanel.getEl() : null);
38675         if(el){
38676             switch(this.position){
38677                 case "east":
38678                 case "west":
38679                     el.setWidth(newSize);
38680                     this.fireEvent("resized", this, newSize);
38681                 break;
38682                 case "north":
38683                 case "south":
38684                     el.setHeight(newSize);
38685                     this.fireEvent("resized", this, newSize);
38686                 break;                
38687             }
38688         }
38689     },
38690     
38691     getBox : function(){
38692         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38693     },
38694     
38695     getMargins : function(){
38696         return this.margins;
38697     },
38698     
38699     updateBox : function(box){
38700         this.box = box;
38701         var el = this.activePanel.getEl();
38702         el.dom.style.left = box.x + "px";
38703         el.dom.style.top = box.y + "px";
38704         this.activePanel.setSize(box.width, box.height);
38705     },
38706     
38707     /**
38708      * Returns the container element for this region.
38709      * @return {Roo.Element}
38710      */
38711     getEl : function(){
38712         return this.activePanel;
38713     },
38714     
38715     /**
38716      * Returns true if this region is currently visible.
38717      * @return {Boolean}
38718      */
38719     isVisible : function(){
38720         return this.activePanel ? true : false;
38721     },
38722     
38723     setActivePanel : function(panel){
38724         panel = this.getPanel(panel);
38725         if(this.activePanel && this.activePanel != panel){
38726             this.activePanel.setActiveState(false);
38727             this.activePanel.getEl().setLeftTop(-10000,-10000);
38728         }
38729         this.activePanel = panel;
38730         panel.setActiveState(true);
38731         if(this.box){
38732             panel.setSize(this.box.width, this.box.height);
38733         }
38734         this.fireEvent("panelactivated", this, panel);
38735         this.fireEvent("invalidated");
38736     },
38737     
38738     /**
38739      * Show the specified panel.
38740      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38741      * @return {Roo.ContentPanel} The shown panel or null
38742      */
38743     showPanel : function(panel){
38744         panel = this.getPanel(panel);
38745         if(panel){
38746             this.setActivePanel(panel);
38747         }
38748         return panel;
38749     },
38750     
38751     /**
38752      * Get the active panel for this region.
38753      * @return {Roo.ContentPanel} The active panel or null
38754      */
38755     getActivePanel : function(){
38756         return this.activePanel;
38757     },
38758     
38759     /**
38760      * Add the passed ContentPanel(s)
38761      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38762      * @return {Roo.ContentPanel} The panel added (if only one was added)
38763      */
38764     add : function(panel){
38765         if(arguments.length > 1){
38766             for(var i = 0, len = arguments.length; i < len; i++) {
38767                 this.add(arguments[i]);
38768             }
38769             return null;
38770         }
38771         if(this.hasPanel(panel)){
38772             this.showPanel(panel);
38773             return panel;
38774         }
38775         var el = panel.getEl();
38776         if(el.dom.parentNode != this.mgr.el.dom){
38777             this.mgr.el.dom.appendChild(el.dom);
38778         }
38779         if(panel.setRegion){
38780             panel.setRegion(this);
38781         }
38782         this.panels.add(panel);
38783         el.setStyle("position", "absolute");
38784         if(!panel.background){
38785             this.setActivePanel(panel);
38786             if(this.config.initialSize && this.panels.getCount()==1){
38787                 this.resizeTo(this.config.initialSize);
38788             }
38789         }
38790         this.fireEvent("paneladded", this, panel);
38791         return panel;
38792     },
38793     
38794     /**
38795      * Returns true if the panel is in this region.
38796      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38797      * @return {Boolean}
38798      */
38799     hasPanel : function(panel){
38800         if(typeof panel == "object"){ // must be panel obj
38801             panel = panel.getId();
38802         }
38803         return this.getPanel(panel) ? true : false;
38804     },
38805     
38806     /**
38807      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38808      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38809      * @param {Boolean} preservePanel Overrides the config preservePanel option
38810      * @return {Roo.ContentPanel} The panel that was removed
38811      */
38812     remove : function(panel, preservePanel){
38813         panel = this.getPanel(panel);
38814         if(!panel){
38815             return null;
38816         }
38817         var e = {};
38818         this.fireEvent("beforeremove", this, panel, e);
38819         if(e.cancel === true){
38820             return null;
38821         }
38822         var panelId = panel.getId();
38823         this.panels.removeKey(panelId);
38824         return panel;
38825     },
38826     
38827     /**
38828      * Returns the panel specified or null if it's not in this region.
38829      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38830      * @return {Roo.ContentPanel}
38831      */
38832     getPanel : function(id){
38833         if(typeof id == "object"){ // must be panel obj
38834             return id;
38835         }
38836         return this.panels.get(id);
38837     },
38838     
38839     /**
38840      * Returns this regions position (north/south/east/west/center).
38841      * @return {String} 
38842      */
38843     getPosition: function(){
38844         return this.position;    
38845     }
38846 });/*
38847  * Based on:
38848  * Ext JS Library 1.1.1
38849  * Copyright(c) 2006-2007, Ext JS, LLC.
38850  *
38851  * Originally Released Under LGPL - original licence link has changed is not relivant.
38852  *
38853  * Fork - LGPL
38854  * <script type="text/javascript">
38855  */
38856  
38857 /**
38858  * @class Roo.bootstrap.layout.Region
38859  * @extends Roo.bootstrap.layout.Basic
38860  * This class represents a region in a layout manager.
38861  
38862  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38863  * @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})
38864  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38865  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38866  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38867  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38868  * @cfg {String}    title           The title for the region (overrides panel titles)
38869  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38870  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38871  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38872  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38873  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38874  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38875  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38876  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38877  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38878  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38879
38880  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38881  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38882  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38883  * @cfg {Number}    width           For East/West panels
38884  * @cfg {Number}    height          For North/South panels
38885  * @cfg {Boolean}   split           To show the splitter
38886  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38887  * 
38888  * @cfg {string}   cls             Extra CSS classes to add to region
38889  * 
38890  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38891  * @cfg {string}   region  the region that it inhabits..
38892  *
38893
38894  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38895  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38896
38897  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38898  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38899  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38900  */
38901 Roo.bootstrap.layout.Region = function(config)
38902 {
38903     this.applyConfig(config);
38904
38905     var mgr = config.mgr;
38906     var pos = config.region;
38907     config.skipConfig = true;
38908     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38909     
38910     if (mgr.el) {
38911         this.onRender(mgr.el);   
38912     }
38913      
38914     this.visible = true;
38915     this.collapsed = false;
38916     this.unrendered_panels = [];
38917 };
38918
38919 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38920
38921     position: '', // set by wrapper (eg. north/south etc..)
38922     unrendered_panels : null,  // unrendered panels.
38923     
38924     tabPosition : false,
38925     
38926     mgr: false, // points to 'Border'
38927     
38928     
38929     createBody : function(){
38930         /** This region's body element 
38931         * @type Roo.Element */
38932         this.bodyEl = this.el.createChild({
38933                 tag: "div",
38934                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38935         });
38936     },
38937
38938     onRender: function(ctr, pos)
38939     {
38940         var dh = Roo.DomHelper;
38941         /** This region's container element 
38942         * @type Roo.Element */
38943         this.el = dh.append(ctr.dom, {
38944                 tag: "div",
38945                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38946             }, true);
38947         /** This region's title element 
38948         * @type Roo.Element */
38949     
38950         this.titleEl = dh.append(this.el.dom,  {
38951                 tag: "div",
38952                 unselectable: "on",
38953                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38954                 children:[
38955                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38956                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38957                 ]
38958             }, true);
38959         
38960         this.titleEl.enableDisplayMode();
38961         /** This region's title text element 
38962         * @type HTMLElement */
38963         this.titleTextEl = this.titleEl.dom.firstChild;
38964         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38965         /*
38966         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38967         this.closeBtn.enableDisplayMode();
38968         this.closeBtn.on("click", this.closeClicked, this);
38969         this.closeBtn.hide();
38970     */
38971         this.createBody(this.config);
38972         if(this.config.hideWhenEmpty){
38973             this.hide();
38974             this.on("paneladded", this.validateVisibility, this);
38975             this.on("panelremoved", this.validateVisibility, this);
38976         }
38977         if(this.autoScroll){
38978             this.bodyEl.setStyle("overflow", "auto");
38979         }else{
38980             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38981         }
38982         //if(c.titlebar !== false){
38983             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38984                 this.titleEl.hide();
38985             }else{
38986                 this.titleEl.show();
38987                 if(this.config.title){
38988                     this.titleTextEl.innerHTML = this.config.title;
38989                 }
38990             }
38991         //}
38992         if(this.config.collapsed){
38993             this.collapse(true);
38994         }
38995         if(this.config.hidden){
38996             this.hide();
38997         }
38998         
38999         if (this.unrendered_panels && this.unrendered_panels.length) {
39000             for (var i =0;i< this.unrendered_panels.length; i++) {
39001                 this.add(this.unrendered_panels[i]);
39002             }
39003             this.unrendered_panels = null;
39004             
39005         }
39006         
39007     },
39008     
39009     applyConfig : function(c)
39010     {
39011         /*
39012          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39013             var dh = Roo.DomHelper;
39014             if(c.titlebar !== false){
39015                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39016                 this.collapseBtn.on("click", this.collapse, this);
39017                 this.collapseBtn.enableDisplayMode();
39018                 /*
39019                 if(c.showPin === true || this.showPin){
39020                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39021                     this.stickBtn.enableDisplayMode();
39022                     this.stickBtn.on("click", this.expand, this);
39023                     this.stickBtn.hide();
39024                 }
39025                 
39026             }
39027             */
39028             /** This region's collapsed element
39029             * @type Roo.Element */
39030             /*
39031              *
39032             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39033                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39034             ]}, true);
39035             
39036             if(c.floatable !== false){
39037                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39038                this.collapsedEl.on("click", this.collapseClick, this);
39039             }
39040
39041             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39042                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39043                    id: "message", unselectable: "on", style:{"float":"left"}});
39044                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39045              }
39046             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39047             this.expandBtn.on("click", this.expand, this);
39048             
39049         }
39050         
39051         if(this.collapseBtn){
39052             this.collapseBtn.setVisible(c.collapsible == true);
39053         }
39054         
39055         this.cmargins = c.cmargins || this.cmargins ||
39056                          (this.position == "west" || this.position == "east" ?
39057                              {top: 0, left: 2, right:2, bottom: 0} :
39058                              {top: 2, left: 0, right:0, bottom: 2});
39059         */
39060         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39061         
39062         
39063         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39064         
39065         this.autoScroll = c.autoScroll || false;
39066         
39067         
39068        
39069         
39070         this.duration = c.duration || .30;
39071         this.slideDuration = c.slideDuration || .45;
39072         this.config = c;
39073        
39074     },
39075     /**
39076      * Returns true if this region is currently visible.
39077      * @return {Boolean}
39078      */
39079     isVisible : function(){
39080         return this.visible;
39081     },
39082
39083     /**
39084      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39085      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39086      */
39087     //setCollapsedTitle : function(title){
39088     //    title = title || "&#160;";
39089      //   if(this.collapsedTitleTextEl){
39090       //      this.collapsedTitleTextEl.innerHTML = title;
39091        // }
39092     //},
39093
39094     getBox : function(){
39095         var b;
39096       //  if(!this.collapsed){
39097             b = this.el.getBox(false, true);
39098        // }else{
39099           //  b = this.collapsedEl.getBox(false, true);
39100         //}
39101         return b;
39102     },
39103
39104     getMargins : function(){
39105         return this.margins;
39106         //return this.collapsed ? this.cmargins : this.margins;
39107     },
39108 /*
39109     highlight : function(){
39110         this.el.addClass("x-layout-panel-dragover");
39111     },
39112
39113     unhighlight : function(){
39114         this.el.removeClass("x-layout-panel-dragover");
39115     },
39116 */
39117     updateBox : function(box)
39118     {
39119         if (!this.bodyEl) {
39120             return; // not rendered yet..
39121         }
39122         
39123         this.box = box;
39124         if(!this.collapsed){
39125             this.el.dom.style.left = box.x + "px";
39126             this.el.dom.style.top = box.y + "px";
39127             this.updateBody(box.width, box.height);
39128         }else{
39129             this.collapsedEl.dom.style.left = box.x + "px";
39130             this.collapsedEl.dom.style.top = box.y + "px";
39131             this.collapsedEl.setSize(box.width, box.height);
39132         }
39133         if(this.tabs){
39134             this.tabs.autoSizeTabs();
39135         }
39136     },
39137
39138     updateBody : function(w, h)
39139     {
39140         if(w !== null){
39141             this.el.setWidth(w);
39142             w -= this.el.getBorderWidth("rl");
39143             if(this.config.adjustments){
39144                 w += this.config.adjustments[0];
39145             }
39146         }
39147         if(h !== null && h > 0){
39148             this.el.setHeight(h);
39149             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39150             h -= this.el.getBorderWidth("tb");
39151             if(this.config.adjustments){
39152                 h += this.config.adjustments[1];
39153             }
39154             this.bodyEl.setHeight(h);
39155             if(this.tabs){
39156                 h = this.tabs.syncHeight(h);
39157             }
39158         }
39159         if(this.panelSize){
39160             w = w !== null ? w : this.panelSize.width;
39161             h = h !== null ? h : this.panelSize.height;
39162         }
39163         if(this.activePanel){
39164             var el = this.activePanel.getEl();
39165             w = w !== null ? w : el.getWidth();
39166             h = h !== null ? h : el.getHeight();
39167             this.panelSize = {width: w, height: h};
39168             this.activePanel.setSize(w, h);
39169         }
39170         if(Roo.isIE && this.tabs){
39171             this.tabs.el.repaint();
39172         }
39173     },
39174
39175     /**
39176      * Returns the container element for this region.
39177      * @return {Roo.Element}
39178      */
39179     getEl : function(){
39180         return this.el;
39181     },
39182
39183     /**
39184      * Hides this region.
39185      */
39186     hide : function(){
39187         //if(!this.collapsed){
39188             this.el.dom.style.left = "-2000px";
39189             this.el.hide();
39190         //}else{
39191          //   this.collapsedEl.dom.style.left = "-2000px";
39192          //   this.collapsedEl.hide();
39193        // }
39194         this.visible = false;
39195         this.fireEvent("visibilitychange", this, false);
39196     },
39197
39198     /**
39199      * Shows this region if it was previously hidden.
39200      */
39201     show : function(){
39202         //if(!this.collapsed){
39203             this.el.show();
39204         //}else{
39205         //    this.collapsedEl.show();
39206        // }
39207         this.visible = true;
39208         this.fireEvent("visibilitychange", this, true);
39209     },
39210 /*
39211     closeClicked : function(){
39212         if(this.activePanel){
39213             this.remove(this.activePanel);
39214         }
39215     },
39216
39217     collapseClick : function(e){
39218         if(this.isSlid){
39219            e.stopPropagation();
39220            this.slideIn();
39221         }else{
39222            e.stopPropagation();
39223            this.slideOut();
39224         }
39225     },
39226 */
39227     /**
39228      * Collapses this region.
39229      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39230      */
39231     /*
39232     collapse : function(skipAnim, skipCheck = false){
39233         if(this.collapsed) {
39234             return;
39235         }
39236         
39237         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39238             
39239             this.collapsed = true;
39240             if(this.split){
39241                 this.split.el.hide();
39242             }
39243             if(this.config.animate && skipAnim !== true){
39244                 this.fireEvent("invalidated", this);
39245                 this.animateCollapse();
39246             }else{
39247                 this.el.setLocation(-20000,-20000);
39248                 this.el.hide();
39249                 this.collapsedEl.show();
39250                 this.fireEvent("collapsed", this);
39251                 this.fireEvent("invalidated", this);
39252             }
39253         }
39254         
39255     },
39256 */
39257     animateCollapse : function(){
39258         // overridden
39259     },
39260
39261     /**
39262      * Expands this region if it was previously collapsed.
39263      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39264      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39265      */
39266     /*
39267     expand : function(e, skipAnim){
39268         if(e) {
39269             e.stopPropagation();
39270         }
39271         if(!this.collapsed || this.el.hasActiveFx()) {
39272             return;
39273         }
39274         if(this.isSlid){
39275             this.afterSlideIn();
39276             skipAnim = true;
39277         }
39278         this.collapsed = false;
39279         if(this.config.animate && skipAnim !== true){
39280             this.animateExpand();
39281         }else{
39282             this.el.show();
39283             if(this.split){
39284                 this.split.el.show();
39285             }
39286             this.collapsedEl.setLocation(-2000,-2000);
39287             this.collapsedEl.hide();
39288             this.fireEvent("invalidated", this);
39289             this.fireEvent("expanded", this);
39290         }
39291     },
39292 */
39293     animateExpand : function(){
39294         // overridden
39295     },
39296
39297     initTabs : function()
39298     {
39299         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39300         
39301         var ts = new Roo.bootstrap.panel.Tabs({
39302             el: this.bodyEl.dom,
39303             region : this,
39304             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39305             disableTooltips: this.config.disableTabTips,
39306             toolbar : this.config.toolbar
39307         });
39308         
39309         if(this.config.hideTabs){
39310             ts.stripWrap.setDisplayed(false);
39311         }
39312         this.tabs = ts;
39313         ts.resizeTabs = this.config.resizeTabs === true;
39314         ts.minTabWidth = this.config.minTabWidth || 40;
39315         ts.maxTabWidth = this.config.maxTabWidth || 250;
39316         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39317         ts.monitorResize = false;
39318         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39319         ts.bodyEl.addClass('roo-layout-tabs-body');
39320         this.panels.each(this.initPanelAsTab, this);
39321     },
39322
39323     initPanelAsTab : function(panel){
39324         var ti = this.tabs.addTab(
39325             panel.getEl().id,
39326             panel.getTitle(),
39327             null,
39328             this.config.closeOnTab && panel.isClosable(),
39329             panel.tpl
39330         );
39331         if(panel.tabTip !== undefined){
39332             ti.setTooltip(panel.tabTip);
39333         }
39334         ti.on("activate", function(){
39335               this.setActivePanel(panel);
39336         }, this);
39337         
39338         if(this.config.closeOnTab){
39339             ti.on("beforeclose", function(t, e){
39340                 e.cancel = true;
39341                 this.remove(panel);
39342             }, this);
39343         }
39344         
39345         panel.tabItem = ti;
39346         
39347         return ti;
39348     },
39349
39350     updatePanelTitle : function(panel, title)
39351     {
39352         if(this.activePanel == panel){
39353             this.updateTitle(title);
39354         }
39355         if(this.tabs){
39356             var ti = this.tabs.getTab(panel.getEl().id);
39357             ti.setText(title);
39358             if(panel.tabTip !== undefined){
39359                 ti.setTooltip(panel.tabTip);
39360             }
39361         }
39362     },
39363
39364     updateTitle : function(title){
39365         if(this.titleTextEl && !this.config.title){
39366             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39367         }
39368     },
39369
39370     setActivePanel : function(panel)
39371     {
39372         panel = this.getPanel(panel);
39373         if(this.activePanel && this.activePanel != panel){
39374             if(this.activePanel.setActiveState(false) === false){
39375                 return;
39376             }
39377         }
39378         this.activePanel = panel;
39379         panel.setActiveState(true);
39380         if(this.panelSize){
39381             panel.setSize(this.panelSize.width, this.panelSize.height);
39382         }
39383         if(this.closeBtn){
39384             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39385         }
39386         this.updateTitle(panel.getTitle());
39387         if(this.tabs){
39388             this.fireEvent("invalidated", this);
39389         }
39390         this.fireEvent("panelactivated", this, panel);
39391     },
39392
39393     /**
39394      * Shows the specified panel.
39395      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39396      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39397      */
39398     showPanel : function(panel)
39399     {
39400         panel = this.getPanel(panel);
39401         if(panel){
39402             if(this.tabs){
39403                 var tab = this.tabs.getTab(panel.getEl().id);
39404                 if(tab.isHidden()){
39405                     this.tabs.unhideTab(tab.id);
39406                 }
39407                 tab.activate();
39408             }else{
39409                 this.setActivePanel(panel);
39410             }
39411         }
39412         return panel;
39413     },
39414
39415     /**
39416      * Get the active panel for this region.
39417      * @return {Roo.ContentPanel} The active panel or null
39418      */
39419     getActivePanel : function(){
39420         return this.activePanel;
39421     },
39422
39423     validateVisibility : function(){
39424         if(this.panels.getCount() < 1){
39425             this.updateTitle("&#160;");
39426             this.closeBtn.hide();
39427             this.hide();
39428         }else{
39429             if(!this.isVisible()){
39430                 this.show();
39431             }
39432         }
39433     },
39434
39435     /**
39436      * Adds the passed ContentPanel(s) to this region.
39437      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39438      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39439      */
39440     add : function(panel)
39441     {
39442         if(arguments.length > 1){
39443             for(var i = 0, len = arguments.length; i < len; i++) {
39444                 this.add(arguments[i]);
39445             }
39446             return null;
39447         }
39448         
39449         // if we have not been rendered yet, then we can not really do much of this..
39450         if (!this.bodyEl) {
39451             this.unrendered_panels.push(panel);
39452             return panel;
39453         }
39454         
39455         
39456         
39457         
39458         if(this.hasPanel(panel)){
39459             this.showPanel(panel);
39460             return panel;
39461         }
39462         panel.setRegion(this);
39463         this.panels.add(panel);
39464        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39465             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39466             // and hide them... ???
39467             this.bodyEl.dom.appendChild(panel.getEl().dom);
39468             if(panel.background !== true){
39469                 this.setActivePanel(panel);
39470             }
39471             this.fireEvent("paneladded", this, panel);
39472             return panel;
39473         }
39474         */
39475         if(!this.tabs){
39476             this.initTabs();
39477         }else{
39478             this.initPanelAsTab(panel);
39479         }
39480         
39481         
39482         if(panel.background !== true){
39483             this.tabs.activate(panel.getEl().id);
39484         }
39485         this.fireEvent("paneladded", this, panel);
39486         return panel;
39487     },
39488
39489     /**
39490      * Hides the tab for the specified panel.
39491      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39492      */
39493     hidePanel : function(panel){
39494         if(this.tabs && (panel = this.getPanel(panel))){
39495             this.tabs.hideTab(panel.getEl().id);
39496         }
39497     },
39498
39499     /**
39500      * Unhides the tab for a previously hidden panel.
39501      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39502      */
39503     unhidePanel : function(panel){
39504         if(this.tabs && (panel = this.getPanel(panel))){
39505             this.tabs.unhideTab(panel.getEl().id);
39506         }
39507     },
39508
39509     clearPanels : function(){
39510         while(this.panels.getCount() > 0){
39511              this.remove(this.panels.first());
39512         }
39513     },
39514
39515     /**
39516      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39517      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39518      * @param {Boolean} preservePanel Overrides the config preservePanel option
39519      * @return {Roo.ContentPanel} The panel that was removed
39520      */
39521     remove : function(panel, preservePanel)
39522     {
39523         panel = this.getPanel(panel);
39524         if(!panel){
39525             return null;
39526         }
39527         var e = {};
39528         this.fireEvent("beforeremove", this, panel, e);
39529         if(e.cancel === true){
39530             return null;
39531         }
39532         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39533         var panelId = panel.getId();
39534         this.panels.removeKey(panelId);
39535         if(preservePanel){
39536             document.body.appendChild(panel.getEl().dom);
39537         }
39538         if(this.tabs){
39539             this.tabs.removeTab(panel.getEl().id);
39540         }else if (!preservePanel){
39541             this.bodyEl.dom.removeChild(panel.getEl().dom);
39542         }
39543         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39544             var p = this.panels.first();
39545             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39546             tempEl.appendChild(p.getEl().dom);
39547             this.bodyEl.update("");
39548             this.bodyEl.dom.appendChild(p.getEl().dom);
39549             tempEl = null;
39550             this.updateTitle(p.getTitle());
39551             this.tabs = null;
39552             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39553             this.setActivePanel(p);
39554         }
39555         panel.setRegion(null);
39556         if(this.activePanel == panel){
39557             this.activePanel = null;
39558         }
39559         if(this.config.autoDestroy !== false && preservePanel !== true){
39560             try{panel.destroy();}catch(e){}
39561         }
39562         this.fireEvent("panelremoved", this, panel);
39563         return panel;
39564     },
39565
39566     /**
39567      * Returns the TabPanel component used by this region
39568      * @return {Roo.TabPanel}
39569      */
39570     getTabs : function(){
39571         return this.tabs;
39572     },
39573
39574     createTool : function(parentEl, className){
39575         var btn = Roo.DomHelper.append(parentEl, {
39576             tag: "div",
39577             cls: "x-layout-tools-button",
39578             children: [ {
39579                 tag: "div",
39580                 cls: "roo-layout-tools-button-inner " + className,
39581                 html: "&#160;"
39582             }]
39583         }, true);
39584         btn.addClassOnOver("roo-layout-tools-button-over");
39585         return btn;
39586     }
39587 });/*
39588  * Based on:
39589  * Ext JS Library 1.1.1
39590  * Copyright(c) 2006-2007, Ext JS, LLC.
39591  *
39592  * Originally Released Under LGPL - original licence link has changed is not relivant.
39593  *
39594  * Fork - LGPL
39595  * <script type="text/javascript">
39596  */
39597  
39598
39599
39600 /**
39601  * @class Roo.SplitLayoutRegion
39602  * @extends Roo.LayoutRegion
39603  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39604  */
39605 Roo.bootstrap.layout.Split = function(config){
39606     this.cursor = config.cursor;
39607     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39608 };
39609
39610 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39611 {
39612     splitTip : "Drag to resize.",
39613     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39614     useSplitTips : false,
39615
39616     applyConfig : function(config){
39617         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39618     },
39619     
39620     onRender : function(ctr,pos) {
39621         
39622         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39623         if(!this.config.split){
39624             return;
39625         }
39626         if(!this.split){
39627             
39628             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39629                             tag: "div",
39630                             id: this.el.id + "-split",
39631                             cls: "roo-layout-split roo-layout-split-"+this.position,
39632                             html: "&#160;"
39633             });
39634             /** The SplitBar for this region 
39635             * @type Roo.SplitBar */
39636             // does not exist yet...
39637             Roo.log([this.position, this.orientation]);
39638             
39639             this.split = new Roo.bootstrap.SplitBar({
39640                 dragElement : splitEl,
39641                 resizingElement: this.el,
39642                 orientation : this.orientation
39643             });
39644             
39645             this.split.on("moved", this.onSplitMove, this);
39646             this.split.useShim = this.config.useShim === true;
39647             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39648             if(this.useSplitTips){
39649                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39650             }
39651             //if(config.collapsible){
39652             //    this.split.el.on("dblclick", this.collapse,  this);
39653             //}
39654         }
39655         if(typeof this.config.minSize != "undefined"){
39656             this.split.minSize = this.config.minSize;
39657         }
39658         if(typeof this.config.maxSize != "undefined"){
39659             this.split.maxSize = this.config.maxSize;
39660         }
39661         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39662             this.hideSplitter();
39663         }
39664         
39665     },
39666
39667     getHMaxSize : function(){
39668          var cmax = this.config.maxSize || 10000;
39669          var center = this.mgr.getRegion("center");
39670          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39671     },
39672
39673     getVMaxSize : function(){
39674          var cmax = this.config.maxSize || 10000;
39675          var center = this.mgr.getRegion("center");
39676          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39677     },
39678
39679     onSplitMove : function(split, newSize){
39680         this.fireEvent("resized", this, newSize);
39681     },
39682     
39683     /** 
39684      * Returns the {@link Roo.SplitBar} for this region.
39685      * @return {Roo.SplitBar}
39686      */
39687     getSplitBar : function(){
39688         return this.split;
39689     },
39690     
39691     hide : function(){
39692         this.hideSplitter();
39693         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39694     },
39695
39696     hideSplitter : function(){
39697         if(this.split){
39698             this.split.el.setLocation(-2000,-2000);
39699             this.split.el.hide();
39700         }
39701     },
39702
39703     show : function(){
39704         if(this.split){
39705             this.split.el.show();
39706         }
39707         Roo.bootstrap.layout.Split.superclass.show.call(this);
39708     },
39709     
39710     beforeSlide: function(){
39711         if(Roo.isGecko){// firefox overflow auto bug workaround
39712             this.bodyEl.clip();
39713             if(this.tabs) {
39714                 this.tabs.bodyEl.clip();
39715             }
39716             if(this.activePanel){
39717                 this.activePanel.getEl().clip();
39718                 
39719                 if(this.activePanel.beforeSlide){
39720                     this.activePanel.beforeSlide();
39721                 }
39722             }
39723         }
39724     },
39725     
39726     afterSlide : function(){
39727         if(Roo.isGecko){// firefox overflow auto bug workaround
39728             this.bodyEl.unclip();
39729             if(this.tabs) {
39730                 this.tabs.bodyEl.unclip();
39731             }
39732             if(this.activePanel){
39733                 this.activePanel.getEl().unclip();
39734                 if(this.activePanel.afterSlide){
39735                     this.activePanel.afterSlide();
39736                 }
39737             }
39738         }
39739     },
39740
39741     initAutoHide : function(){
39742         if(this.autoHide !== false){
39743             if(!this.autoHideHd){
39744                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39745                 this.autoHideHd = {
39746                     "mouseout": function(e){
39747                         if(!e.within(this.el, true)){
39748                             st.delay(500);
39749                         }
39750                     },
39751                     "mouseover" : function(e){
39752                         st.cancel();
39753                     },
39754                     scope : this
39755                 };
39756             }
39757             this.el.on(this.autoHideHd);
39758         }
39759     },
39760
39761     clearAutoHide : function(){
39762         if(this.autoHide !== false){
39763             this.el.un("mouseout", this.autoHideHd.mouseout);
39764             this.el.un("mouseover", this.autoHideHd.mouseover);
39765         }
39766     },
39767
39768     clearMonitor : function(){
39769         Roo.get(document).un("click", this.slideInIf, this);
39770     },
39771
39772     // these names are backwards but not changed for compat
39773     slideOut : function(){
39774         if(this.isSlid || this.el.hasActiveFx()){
39775             return;
39776         }
39777         this.isSlid = true;
39778         if(this.collapseBtn){
39779             this.collapseBtn.hide();
39780         }
39781         this.closeBtnState = this.closeBtn.getStyle('display');
39782         this.closeBtn.hide();
39783         if(this.stickBtn){
39784             this.stickBtn.show();
39785         }
39786         this.el.show();
39787         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39788         this.beforeSlide();
39789         this.el.setStyle("z-index", 10001);
39790         this.el.slideIn(this.getSlideAnchor(), {
39791             callback: function(){
39792                 this.afterSlide();
39793                 this.initAutoHide();
39794                 Roo.get(document).on("click", this.slideInIf, this);
39795                 this.fireEvent("slideshow", this);
39796             },
39797             scope: this,
39798             block: true
39799         });
39800     },
39801
39802     afterSlideIn : function(){
39803         this.clearAutoHide();
39804         this.isSlid = false;
39805         this.clearMonitor();
39806         this.el.setStyle("z-index", "");
39807         if(this.collapseBtn){
39808             this.collapseBtn.show();
39809         }
39810         this.closeBtn.setStyle('display', this.closeBtnState);
39811         if(this.stickBtn){
39812             this.stickBtn.hide();
39813         }
39814         this.fireEvent("slidehide", this);
39815     },
39816
39817     slideIn : function(cb){
39818         if(!this.isSlid || this.el.hasActiveFx()){
39819             Roo.callback(cb);
39820             return;
39821         }
39822         this.isSlid = false;
39823         this.beforeSlide();
39824         this.el.slideOut(this.getSlideAnchor(), {
39825             callback: function(){
39826                 this.el.setLeftTop(-10000, -10000);
39827                 this.afterSlide();
39828                 this.afterSlideIn();
39829                 Roo.callback(cb);
39830             },
39831             scope: this,
39832             block: true
39833         });
39834     },
39835     
39836     slideInIf : function(e){
39837         if(!e.within(this.el)){
39838             this.slideIn();
39839         }
39840     },
39841
39842     animateCollapse : function(){
39843         this.beforeSlide();
39844         this.el.setStyle("z-index", 20000);
39845         var anchor = this.getSlideAnchor();
39846         this.el.slideOut(anchor, {
39847             callback : function(){
39848                 this.el.setStyle("z-index", "");
39849                 this.collapsedEl.slideIn(anchor, {duration:.3});
39850                 this.afterSlide();
39851                 this.el.setLocation(-10000,-10000);
39852                 this.el.hide();
39853                 this.fireEvent("collapsed", this);
39854             },
39855             scope: this,
39856             block: true
39857         });
39858     },
39859
39860     animateExpand : function(){
39861         this.beforeSlide();
39862         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39863         this.el.setStyle("z-index", 20000);
39864         this.collapsedEl.hide({
39865             duration:.1
39866         });
39867         this.el.slideIn(this.getSlideAnchor(), {
39868             callback : function(){
39869                 this.el.setStyle("z-index", "");
39870                 this.afterSlide();
39871                 if(this.split){
39872                     this.split.el.show();
39873                 }
39874                 this.fireEvent("invalidated", this);
39875                 this.fireEvent("expanded", this);
39876             },
39877             scope: this,
39878             block: true
39879         });
39880     },
39881
39882     anchors : {
39883         "west" : "left",
39884         "east" : "right",
39885         "north" : "top",
39886         "south" : "bottom"
39887     },
39888
39889     sanchors : {
39890         "west" : "l",
39891         "east" : "r",
39892         "north" : "t",
39893         "south" : "b"
39894     },
39895
39896     canchors : {
39897         "west" : "tl-tr",
39898         "east" : "tr-tl",
39899         "north" : "tl-bl",
39900         "south" : "bl-tl"
39901     },
39902
39903     getAnchor : function(){
39904         return this.anchors[this.position];
39905     },
39906
39907     getCollapseAnchor : function(){
39908         return this.canchors[this.position];
39909     },
39910
39911     getSlideAnchor : function(){
39912         return this.sanchors[this.position];
39913     },
39914
39915     getAlignAdj : function(){
39916         var cm = this.cmargins;
39917         switch(this.position){
39918             case "west":
39919                 return [0, 0];
39920             break;
39921             case "east":
39922                 return [0, 0];
39923             break;
39924             case "north":
39925                 return [0, 0];
39926             break;
39927             case "south":
39928                 return [0, 0];
39929             break;
39930         }
39931     },
39932
39933     getExpandAdj : function(){
39934         var c = this.collapsedEl, cm = this.cmargins;
39935         switch(this.position){
39936             case "west":
39937                 return [-(cm.right+c.getWidth()+cm.left), 0];
39938             break;
39939             case "east":
39940                 return [cm.right+c.getWidth()+cm.left, 0];
39941             break;
39942             case "north":
39943                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39944             break;
39945             case "south":
39946                 return [0, cm.top+cm.bottom+c.getHeight()];
39947             break;
39948         }
39949     }
39950 });/*
39951  * Based on:
39952  * Ext JS Library 1.1.1
39953  * Copyright(c) 2006-2007, Ext JS, LLC.
39954  *
39955  * Originally Released Under LGPL - original licence link has changed is not relivant.
39956  *
39957  * Fork - LGPL
39958  * <script type="text/javascript">
39959  */
39960 /*
39961  * These classes are private internal classes
39962  */
39963 Roo.bootstrap.layout.Center = function(config){
39964     config.region = "center";
39965     Roo.bootstrap.layout.Region.call(this, config);
39966     this.visible = true;
39967     this.minWidth = config.minWidth || 20;
39968     this.minHeight = config.minHeight || 20;
39969 };
39970
39971 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39972     hide : function(){
39973         // center panel can't be hidden
39974     },
39975     
39976     show : function(){
39977         // center panel can't be hidden
39978     },
39979     
39980     getMinWidth: function(){
39981         return this.minWidth;
39982     },
39983     
39984     getMinHeight: function(){
39985         return this.minHeight;
39986     }
39987 });
39988
39989
39990
39991
39992  
39993
39994
39995
39996
39997
39998
39999 Roo.bootstrap.layout.North = function(config)
40000 {
40001     config.region = 'north';
40002     config.cursor = 'n-resize';
40003     
40004     Roo.bootstrap.layout.Split.call(this, config);
40005     
40006     
40007     if(this.split){
40008         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40009         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40010         this.split.el.addClass("roo-layout-split-v");
40011     }
40012     //var size = config.initialSize || config.height;
40013     //if(this.el && typeof size != "undefined"){
40014     //    this.el.setHeight(size);
40015     //}
40016 };
40017 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40018 {
40019     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40020      
40021      
40022     onRender : function(ctr, pos)
40023     {
40024         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40025         var size = this.config.initialSize || this.config.height;
40026         if(this.el && typeof size != "undefined"){
40027             this.el.setHeight(size);
40028         }
40029     
40030     },
40031     
40032     getBox : function(){
40033         if(this.collapsed){
40034             return this.collapsedEl.getBox();
40035         }
40036         var box = this.el.getBox();
40037         if(this.split){
40038             box.height += this.split.el.getHeight();
40039         }
40040         return box;
40041     },
40042     
40043     updateBox : function(box){
40044         if(this.split && !this.collapsed){
40045             box.height -= this.split.el.getHeight();
40046             this.split.el.setLeft(box.x);
40047             this.split.el.setTop(box.y+box.height);
40048             this.split.el.setWidth(box.width);
40049         }
40050         if(this.collapsed){
40051             this.updateBody(box.width, null);
40052         }
40053         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40054     }
40055 });
40056
40057
40058
40059
40060
40061 Roo.bootstrap.layout.South = function(config){
40062     config.region = 'south';
40063     config.cursor = 's-resize';
40064     Roo.bootstrap.layout.Split.call(this, config);
40065     if(this.split){
40066         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40067         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40068         this.split.el.addClass("roo-layout-split-v");
40069     }
40070     
40071 };
40072
40073 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40074     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40075     
40076     onRender : function(ctr, pos)
40077     {
40078         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40079         var size = this.config.initialSize || this.config.height;
40080         if(this.el && typeof size != "undefined"){
40081             this.el.setHeight(size);
40082         }
40083     
40084     },
40085     
40086     getBox : function(){
40087         if(this.collapsed){
40088             return this.collapsedEl.getBox();
40089         }
40090         var box = this.el.getBox();
40091         if(this.split){
40092             var sh = this.split.el.getHeight();
40093             box.height += sh;
40094             box.y -= sh;
40095         }
40096         return box;
40097     },
40098     
40099     updateBox : function(box){
40100         if(this.split && !this.collapsed){
40101             var sh = this.split.el.getHeight();
40102             box.height -= sh;
40103             box.y += sh;
40104             this.split.el.setLeft(box.x);
40105             this.split.el.setTop(box.y-sh);
40106             this.split.el.setWidth(box.width);
40107         }
40108         if(this.collapsed){
40109             this.updateBody(box.width, null);
40110         }
40111         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40112     }
40113 });
40114
40115 Roo.bootstrap.layout.East = function(config){
40116     config.region = "east";
40117     config.cursor = "e-resize";
40118     Roo.bootstrap.layout.Split.call(this, config);
40119     if(this.split){
40120         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40121         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40122         this.split.el.addClass("roo-layout-split-h");
40123     }
40124     
40125 };
40126 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40127     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40128     
40129     onRender : function(ctr, pos)
40130     {
40131         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40132         var size = this.config.initialSize || this.config.width;
40133         if(this.el && typeof size != "undefined"){
40134             this.el.setWidth(size);
40135         }
40136     
40137     },
40138     
40139     getBox : function(){
40140         if(this.collapsed){
40141             return this.collapsedEl.getBox();
40142         }
40143         var box = this.el.getBox();
40144         if(this.split){
40145             var sw = this.split.el.getWidth();
40146             box.width += sw;
40147             box.x -= sw;
40148         }
40149         return box;
40150     },
40151
40152     updateBox : function(box){
40153         if(this.split && !this.collapsed){
40154             var sw = this.split.el.getWidth();
40155             box.width -= sw;
40156             this.split.el.setLeft(box.x);
40157             this.split.el.setTop(box.y);
40158             this.split.el.setHeight(box.height);
40159             box.x += sw;
40160         }
40161         if(this.collapsed){
40162             this.updateBody(null, box.height);
40163         }
40164         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40165     }
40166 });
40167
40168 Roo.bootstrap.layout.West = function(config){
40169     config.region = "west";
40170     config.cursor = "w-resize";
40171     
40172     Roo.bootstrap.layout.Split.call(this, config);
40173     if(this.split){
40174         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40175         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40176         this.split.el.addClass("roo-layout-split-h");
40177     }
40178     
40179 };
40180 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40181     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40182     
40183     onRender: function(ctr, pos)
40184     {
40185         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40186         var size = this.config.initialSize || this.config.width;
40187         if(typeof size != "undefined"){
40188             this.el.setWidth(size);
40189         }
40190     },
40191     
40192     getBox : function(){
40193         if(this.collapsed){
40194             return this.collapsedEl.getBox();
40195         }
40196         var box = this.el.getBox();
40197         if (box.width == 0) {
40198             box.width = this.config.width; // kludge?
40199         }
40200         if(this.split){
40201             box.width += this.split.el.getWidth();
40202         }
40203         return box;
40204     },
40205     
40206     updateBox : function(box){
40207         if(this.split && !this.collapsed){
40208             var sw = this.split.el.getWidth();
40209             box.width -= sw;
40210             this.split.el.setLeft(box.x+box.width);
40211             this.split.el.setTop(box.y);
40212             this.split.el.setHeight(box.height);
40213         }
40214         if(this.collapsed){
40215             this.updateBody(null, box.height);
40216         }
40217         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40218     }
40219 });/*
40220  * Based on:
40221  * Ext JS Library 1.1.1
40222  * Copyright(c) 2006-2007, Ext JS, LLC.
40223  *
40224  * Originally Released Under LGPL - original licence link has changed is not relivant.
40225  *
40226  * Fork - LGPL
40227  * <script type="text/javascript">
40228  */
40229 /**
40230  * @class Roo.bootstrap.paenl.Content
40231  * @extends Roo.util.Observable
40232  * @children Roo.bootstrap.Component
40233  * @parent builder Roo.bootstrap.layout.Border
40234  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40235  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40236  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40237  * @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
40238  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40239  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40240  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40241  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40242  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40243  * @cfg {String} title          The title for this panel
40244  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40245  * @cfg {String} url            Calls {@link #setUrl} with this value
40246  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40247  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40248  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40249  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40250  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40251  * @cfg {Boolean} badges render the badges
40252  * @cfg {String} cls  extra classes to use  
40253  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40254  
40255  * @constructor
40256  * Create a new ContentPanel.
40257  * @param {String/Object} config A string to set only the title or a config object
40258  
40259  */
40260 Roo.bootstrap.panel.Content = function( config){
40261     
40262     this.tpl = config.tpl || false;
40263     
40264     var el = config.el;
40265     var content = config.content;
40266
40267     if(config.autoCreate){ // xtype is available if this is called from factory
40268         el = Roo.id();
40269     }
40270     this.el = Roo.get(el);
40271     if(!this.el && config && config.autoCreate){
40272         if(typeof config.autoCreate == "object"){
40273             if(!config.autoCreate.id){
40274                 config.autoCreate.id = config.id||el;
40275             }
40276             this.el = Roo.DomHelper.append(document.body,
40277                         config.autoCreate, true);
40278         }else{
40279             var elcfg =  {
40280                 tag: "div",
40281                 cls: (config.cls || '') +
40282                     (config.background ? ' bg-' + config.background : '') +
40283                     " roo-layout-inactive-content",
40284                 id: config.id||el
40285             };
40286             if (config.iframe) {
40287                 elcfg.cn = [
40288                     {
40289                         tag : 'iframe',
40290                         style : 'border: 0px',
40291                         src : 'about:blank'
40292                     }
40293                 ];
40294             }
40295               
40296             if (config.html) {
40297                 elcfg.html = config.html;
40298                 
40299             }
40300                         
40301             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40302             if (config.iframe) {
40303                 this.iframeEl = this.el.select('iframe',true).first();
40304             }
40305             
40306         }
40307     } 
40308     this.closable = false;
40309     this.loaded = false;
40310     this.active = false;
40311    
40312       
40313     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40314         
40315         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40316         
40317         this.wrapEl = this.el; //this.el.wrap();
40318         var ti = [];
40319         if (config.toolbar.items) {
40320             ti = config.toolbar.items ;
40321             delete config.toolbar.items ;
40322         }
40323         
40324         var nitems = [];
40325         this.toolbar.render(this.wrapEl, 'before');
40326         for(var i =0;i < ti.length;i++) {
40327           //  Roo.log(['add child', items[i]]);
40328             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40329         }
40330         this.toolbar.items = nitems;
40331         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40332         delete config.toolbar;
40333         
40334     }
40335     /*
40336     // xtype created footer. - not sure if will work as we normally have to render first..
40337     if (this.footer && !this.footer.el && this.footer.xtype) {
40338         if (!this.wrapEl) {
40339             this.wrapEl = this.el.wrap();
40340         }
40341     
40342         this.footer.container = this.wrapEl.createChild();
40343          
40344         this.footer = Roo.factory(this.footer, Roo);
40345         
40346     }
40347     */
40348     
40349      if(typeof config == "string"){
40350         this.title = config;
40351     }else{
40352         Roo.apply(this, config);
40353     }
40354     
40355     if(this.resizeEl){
40356         this.resizeEl = Roo.get(this.resizeEl, true);
40357     }else{
40358         this.resizeEl = this.el;
40359     }
40360     // handle view.xtype
40361     
40362  
40363     
40364     
40365     this.addEvents({
40366         /**
40367          * @event activate
40368          * Fires when this panel is activated. 
40369          * @param {Roo.ContentPanel} this
40370          */
40371         "activate" : true,
40372         /**
40373          * @event deactivate
40374          * Fires when this panel is activated. 
40375          * @param {Roo.ContentPanel} this
40376          */
40377         "deactivate" : true,
40378
40379         /**
40380          * @event resize
40381          * Fires when this panel is resized if fitToFrame is true.
40382          * @param {Roo.ContentPanel} this
40383          * @param {Number} width The width after any component adjustments
40384          * @param {Number} height The height after any component adjustments
40385          */
40386         "resize" : true,
40387         
40388          /**
40389          * @event render
40390          * Fires when this tab is created
40391          * @param {Roo.ContentPanel} this
40392          */
40393         "render" : true,
40394         
40395           /**
40396          * @event scroll
40397          * Fires when this content is scrolled
40398          * @param {Roo.ContentPanel} this
40399          * @param {Event} scrollEvent
40400          */
40401         "scroll" : true
40402         
40403         
40404         
40405     });
40406     
40407
40408     
40409     
40410     if(this.autoScroll && !this.iframe){
40411         this.resizeEl.setStyle("overflow", "auto");
40412         this.resizeEl.on('scroll', this.onScroll, this);
40413     } else {
40414         // fix randome scrolling
40415         //this.el.on('scroll', function() {
40416         //    Roo.log('fix random scolling');
40417         //    this.scrollTo('top',0); 
40418         //});
40419     }
40420     content = content || this.content;
40421     if(content){
40422         this.setContent(content);
40423     }
40424     if(config && config.url){
40425         this.setUrl(this.url, this.params, this.loadOnce);
40426     }
40427     
40428     
40429     
40430     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40431     
40432     if (this.view && typeof(this.view.xtype) != 'undefined') {
40433         this.view.el = this.el.appendChild(document.createElement("div"));
40434         this.view = Roo.factory(this.view); 
40435         this.view.render  &&  this.view.render(false, '');  
40436     }
40437     
40438     
40439     this.fireEvent('render', this);
40440 };
40441
40442 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40443     
40444     cls : '',
40445     background : '',
40446     
40447     tabTip : '',
40448     
40449     iframe : false,
40450     iframeEl : false,
40451     
40452     /* Resize Element - use this to work out scroll etc. */
40453     resizeEl : false,
40454     
40455     setRegion : function(region){
40456         this.region = region;
40457         this.setActiveClass(region && !this.background);
40458     },
40459     
40460     
40461     setActiveClass: function(state)
40462     {
40463         if(state){
40464            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40465            this.el.setStyle('position','relative');
40466         }else{
40467            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40468            this.el.setStyle('position', 'absolute');
40469         } 
40470     },
40471     
40472     /**
40473      * Returns the toolbar for this Panel if one was configured. 
40474      * @return {Roo.Toolbar} 
40475      */
40476     getToolbar : function(){
40477         return this.toolbar;
40478     },
40479     
40480     setActiveState : function(active)
40481     {
40482         this.active = active;
40483         this.setActiveClass(active);
40484         if(!active){
40485             if(this.fireEvent("deactivate", this) === false){
40486                 return false;
40487             }
40488             return true;
40489         }
40490         this.fireEvent("activate", this);
40491         return true;
40492     },
40493     /**
40494      * Updates this panel's element (not for iframe)
40495      * @param {String} content The new content
40496      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40497     */
40498     setContent : function(content, loadScripts){
40499         if (this.iframe) {
40500             return;
40501         }
40502         
40503         this.el.update(content, loadScripts);
40504     },
40505
40506     ignoreResize : function(w, h)
40507     {
40508         return false; // always resize?
40509         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40510             return true;
40511         }else{
40512             this.lastSize = {width: w, height: h};
40513             return false;
40514         }
40515     },
40516     /**
40517      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40518      * @return {Roo.UpdateManager} The UpdateManager
40519      */
40520     getUpdateManager : function(){
40521         if (this.iframe) {
40522             return false;
40523         }
40524         return this.el.getUpdateManager();
40525     },
40526      /**
40527      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40528      * Does not work with IFRAME contents
40529      * @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:
40530 <pre><code>
40531 panel.load({
40532     url: "your-url.php",
40533     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40534     callback: yourFunction,
40535     scope: yourObject, //(optional scope)
40536     discardUrl: false,
40537     nocache: false,
40538     text: "Loading...",
40539     timeout: 30,
40540     scripts: false
40541 });
40542 </code></pre>
40543      
40544      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40545      * 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.
40546      * @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}
40547      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40548      * @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.
40549      * @return {Roo.ContentPanel} this
40550      */
40551     load : function(){
40552         
40553         if (this.iframe) {
40554             return this;
40555         }
40556         
40557         var um = this.el.getUpdateManager();
40558         um.update.apply(um, arguments);
40559         return this;
40560     },
40561
40562
40563     /**
40564      * 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.
40565      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40566      * @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)
40567      * @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)
40568      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40569      */
40570     setUrl : function(url, params, loadOnce){
40571         if (this.iframe) {
40572             this.iframeEl.dom.src = url;
40573             return false;
40574         }
40575         
40576         if(this.refreshDelegate){
40577             this.removeListener("activate", this.refreshDelegate);
40578         }
40579         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40580         this.on("activate", this.refreshDelegate);
40581         return this.el.getUpdateManager();
40582     },
40583     
40584     _handleRefresh : function(url, params, loadOnce){
40585         if(!loadOnce || !this.loaded){
40586             var updater = this.el.getUpdateManager();
40587             updater.update(url, params, this._setLoaded.createDelegate(this));
40588         }
40589     },
40590     
40591     _setLoaded : function(){
40592         this.loaded = true;
40593     }, 
40594     
40595     /**
40596      * Returns this panel's id
40597      * @return {String} 
40598      */
40599     getId : function(){
40600         return this.el.id;
40601     },
40602     
40603     /** 
40604      * Returns this panel's element - used by regiosn to add.
40605      * @return {Roo.Element} 
40606      */
40607     getEl : function(){
40608         return this.wrapEl || this.el;
40609     },
40610     
40611    
40612     
40613     adjustForComponents : function(width, height)
40614     {
40615         //Roo.log('adjustForComponents ');
40616         if(this.resizeEl != this.el){
40617             width -= this.el.getFrameWidth('lr');
40618             height -= this.el.getFrameWidth('tb');
40619         }
40620         if(this.toolbar){
40621             var te = this.toolbar.getEl();
40622             te.setWidth(width);
40623             height -= te.getHeight();
40624         }
40625         if(this.footer){
40626             var te = this.footer.getEl();
40627             te.setWidth(width);
40628             height -= te.getHeight();
40629         }
40630         
40631         
40632         if(this.adjustments){
40633             width += this.adjustments[0];
40634             height += this.adjustments[1];
40635         }
40636         return {"width": width, "height": height};
40637     },
40638     
40639     setSize : function(width, height){
40640         if(this.fitToFrame && !this.ignoreResize(width, height)){
40641             if(this.fitContainer && this.resizeEl != this.el){
40642                 this.el.setSize(width, height);
40643             }
40644             var size = this.adjustForComponents(width, height);
40645             if (this.iframe) {
40646                 this.iframeEl.setSize(width,height);
40647             }
40648             
40649             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40650             this.fireEvent('resize', this, size.width, size.height);
40651             
40652             
40653         }
40654     },
40655     
40656     /**
40657      * Returns this panel's title
40658      * @return {String} 
40659      */
40660     getTitle : function(){
40661         
40662         if (typeof(this.title) != 'object') {
40663             return this.title;
40664         }
40665         
40666         var t = '';
40667         for (var k in this.title) {
40668             if (!this.title.hasOwnProperty(k)) {
40669                 continue;
40670             }
40671             
40672             if (k.indexOf('-') >= 0) {
40673                 var s = k.split('-');
40674                 for (var i = 0; i<s.length; i++) {
40675                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40676                 }
40677             } else {
40678                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40679             }
40680         }
40681         return t;
40682     },
40683     
40684     /**
40685      * Set this panel's title
40686      * @param {String} title
40687      */
40688     setTitle : function(title){
40689         this.title = title;
40690         if(this.region){
40691             this.region.updatePanelTitle(this, title);
40692         }
40693     },
40694     
40695     /**
40696      * Returns true is this panel was configured to be closable
40697      * @return {Boolean} 
40698      */
40699     isClosable : function(){
40700         return this.closable;
40701     },
40702     
40703     beforeSlide : function(){
40704         this.el.clip();
40705         this.resizeEl.clip();
40706     },
40707     
40708     afterSlide : function(){
40709         this.el.unclip();
40710         this.resizeEl.unclip();
40711     },
40712     
40713     /**
40714      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40715      *   Will fail silently if the {@link #setUrl} method has not been called.
40716      *   This does not activate the panel, just updates its content.
40717      */
40718     refresh : function(){
40719         if(this.refreshDelegate){
40720            this.loaded = false;
40721            this.refreshDelegate();
40722         }
40723     },
40724     
40725     /**
40726      * Destroys this panel
40727      */
40728     destroy : function(){
40729         this.el.removeAllListeners();
40730         var tempEl = document.createElement("span");
40731         tempEl.appendChild(this.el.dom);
40732         tempEl.innerHTML = "";
40733         this.el.remove();
40734         this.el = null;
40735     },
40736     
40737     /**
40738      * form - if the content panel contains a form - this is a reference to it.
40739      * @type {Roo.form.Form}
40740      */
40741     form : false,
40742     /**
40743      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40744      *    This contains a reference to it.
40745      * @type {Roo.View}
40746      */
40747     view : false,
40748     
40749       /**
40750      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40751      * <pre><code>
40752
40753 layout.addxtype({
40754        xtype : 'Form',
40755        items: [ .... ]
40756    }
40757 );
40758
40759 </code></pre>
40760      * @param {Object} cfg Xtype definition of item to add.
40761      */
40762     
40763     
40764     getChildContainer: function () {
40765         return this.getEl();
40766     },
40767     
40768     
40769     onScroll : function(e)
40770     {
40771         this.fireEvent('scroll', this, e);
40772     }
40773     
40774     
40775     /*
40776         var  ret = new Roo.factory(cfg);
40777         return ret;
40778         
40779         
40780         // add form..
40781         if (cfg.xtype.match(/^Form$/)) {
40782             
40783             var el;
40784             //if (this.footer) {
40785             //    el = this.footer.container.insertSibling(false, 'before');
40786             //} else {
40787                 el = this.el.createChild();
40788             //}
40789
40790             this.form = new  Roo.form.Form(cfg);
40791             
40792             
40793             if ( this.form.allItems.length) {
40794                 this.form.render(el.dom);
40795             }
40796             return this.form;
40797         }
40798         // should only have one of theses..
40799         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40800             // views.. should not be just added - used named prop 'view''
40801             
40802             cfg.el = this.el.appendChild(document.createElement("div"));
40803             // factory?
40804             
40805             var ret = new Roo.factory(cfg);
40806              
40807              ret.render && ret.render(false, ''); // render blank..
40808             this.view = ret;
40809             return ret;
40810         }
40811         return false;
40812     }
40813     \*/
40814 });
40815  
40816 /**
40817  * @class Roo.bootstrap.panel.Grid
40818  * @extends Roo.bootstrap.panel.Content
40819  * @constructor
40820  * Create a new GridPanel.
40821  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40822  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40823  * @param {Object} config A the config object
40824   
40825  */
40826
40827
40828
40829 Roo.bootstrap.panel.Grid = function(config)
40830 {
40831     
40832       
40833     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40834         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40835
40836     config.el = this.wrapper;
40837     //this.el = this.wrapper;
40838     
40839       if (config.container) {
40840         // ctor'ed from a Border/panel.grid
40841         
40842         
40843         this.wrapper.setStyle("overflow", "hidden");
40844         this.wrapper.addClass('roo-grid-container');
40845
40846     }
40847     
40848     
40849     if(config.toolbar){
40850         var tool_el = this.wrapper.createChild();    
40851         this.toolbar = Roo.factory(config.toolbar);
40852         var ti = [];
40853         if (config.toolbar.items) {
40854             ti = config.toolbar.items ;
40855             delete config.toolbar.items ;
40856         }
40857         
40858         var nitems = [];
40859         this.toolbar.render(tool_el);
40860         for(var i =0;i < ti.length;i++) {
40861           //  Roo.log(['add child', items[i]]);
40862             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40863         }
40864         this.toolbar.items = nitems;
40865         
40866         delete config.toolbar;
40867     }
40868     
40869     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40870     config.grid.scrollBody = true;;
40871     config.grid.monitorWindowResize = false; // turn off autosizing
40872     config.grid.autoHeight = false;
40873     config.grid.autoWidth = false;
40874     
40875     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40876     
40877     if (config.background) {
40878         // render grid on panel activation (if panel background)
40879         this.on('activate', function(gp) {
40880             if (!gp.grid.rendered) {
40881                 gp.grid.render(this.wrapper);
40882                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40883             }
40884         });
40885             
40886     } else {
40887         this.grid.render(this.wrapper);
40888         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40889
40890     }
40891     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40892     // ??? needed ??? config.el = this.wrapper;
40893     
40894     
40895     
40896   
40897     // xtype created footer. - not sure if will work as we normally have to render first..
40898     if (this.footer && !this.footer.el && this.footer.xtype) {
40899         
40900         var ctr = this.grid.getView().getFooterPanel(true);
40901         this.footer.dataSource = this.grid.dataSource;
40902         this.footer = Roo.factory(this.footer, Roo);
40903         this.footer.render(ctr);
40904         
40905     }
40906     
40907     
40908     
40909     
40910      
40911 };
40912
40913 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
40914 {
40915     // private
40916     is_resizing : false,
40917     
40918     getId : function(){
40919         return this.grid.id;
40920     },
40921     
40922     /**
40923      * Returns the grid for this panel
40924      * @return {Roo.bootstrap.Table} 
40925      */
40926     getGrid : function(){
40927         return this.grid;    
40928     },
40929     
40930     setSize : function(width, height)
40931     {
40932         if (this.is_resizing) {
40933             return;
40934         
40935         }
40936         this.is_resizing = true;
40937         if(!this.ignoreResize(width, height)){
40938             var grid = this.grid;
40939             var size = this.adjustForComponents(width, height);
40940             // tfoot is not a footer?
40941           
40942             
40943             var gridel = grid.getGridEl();
40944             gridel.setSize(size.width, size.height);
40945             
40946             var tbd = grid.getGridEl().select('tbody', true).first();
40947             var thd = grid.getGridEl().select('thead',true).first();
40948             var tbf= grid.getGridEl().select('tfoot', true).first();
40949
40950             if (tbf) {
40951                 size.height -= tbf.getHeight();
40952             }
40953             if (thd) {
40954                 size.height -= thd.getHeight();
40955             }
40956             
40957             tbd.setSize(size.width, size.height );
40958             // this is for the account management tab -seems to work there.
40959             var thd = grid.getGridEl().select('thead',true).first();
40960             //if (tbd) {
40961             //    tbd.setSize(size.width, size.height - thd.getHeight());
40962             //}
40963              
40964             grid.autoSize();
40965         }
40966         this.is_resizing = false;
40967     },
40968      
40969     
40970     
40971     beforeSlide : function(){
40972         this.grid.getView().scroller.clip();
40973     },
40974     
40975     afterSlide : function(){
40976         this.grid.getView().scroller.unclip();
40977     },
40978     
40979     destroy : function(){
40980         this.grid.destroy();
40981         delete this.grid;
40982         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40983     }
40984 });
40985
40986 /**
40987  * @class Roo.bootstrap.panel.Nest
40988  * @extends Roo.bootstrap.panel.Content
40989  * @constructor
40990  * Create a new Panel, that can contain a layout.Border.
40991  * 
40992  * 
40993  * @param {String/Object} config A string to set only the title or a config object
40994  */
40995 Roo.bootstrap.panel.Nest = function(config)
40996 {
40997     // construct with only one argument..
40998     /* FIXME - implement nicer consturctors
40999     if (layout.layout) {
41000         config = layout;
41001         layout = config.layout;
41002         delete config.layout;
41003     }
41004     if (layout.xtype && !layout.getEl) {
41005         // then layout needs constructing..
41006         layout = Roo.factory(layout, Roo);
41007     }
41008     */
41009     
41010     config.el =  config.layout.getEl();
41011     
41012     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41013     
41014     config.layout.monitorWindowResize = false; // turn off autosizing
41015     this.layout = config.layout;
41016     this.layout.getEl().addClass("roo-layout-nested-layout");
41017     this.layout.parent = this;
41018     
41019     
41020     
41021     
41022 };
41023
41024 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41025     /**
41026     * @cfg {Roo.BorderLayout} layout The layout for this panel
41027     */
41028     layout : false,
41029
41030     setSize : function(width, height){
41031         if(!this.ignoreResize(width, height)){
41032             var size = this.adjustForComponents(width, height);
41033             var el = this.layout.getEl();
41034             if (size.height < 1) {
41035                 el.setWidth(size.width);   
41036             } else {
41037                 el.setSize(size.width, size.height);
41038             }
41039             var touch = el.dom.offsetWidth;
41040             this.layout.layout();
41041             // ie requires a double layout on the first pass
41042             if(Roo.isIE && !this.initialized){
41043                 this.initialized = true;
41044                 this.layout.layout();
41045             }
41046         }
41047     },
41048     
41049     // activate all subpanels if not currently active..
41050     
41051     setActiveState : function(active){
41052         this.active = active;
41053         this.setActiveClass(active);
41054         
41055         if(!active){
41056             this.fireEvent("deactivate", this);
41057             return;
41058         }
41059         
41060         this.fireEvent("activate", this);
41061         // not sure if this should happen before or after..
41062         if (!this.layout) {
41063             return; // should not happen..
41064         }
41065         var reg = false;
41066         for (var r in this.layout.regions) {
41067             reg = this.layout.getRegion(r);
41068             if (reg.getActivePanel()) {
41069                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41070                 reg.setActivePanel(reg.getActivePanel());
41071                 continue;
41072             }
41073             if (!reg.panels.length) {
41074                 continue;
41075             }
41076             reg.showPanel(reg.getPanel(0));
41077         }
41078         
41079         
41080         
41081         
41082     },
41083     
41084     /**
41085      * Returns the nested BorderLayout for this panel
41086      * @return {Roo.BorderLayout} 
41087      */
41088     getLayout : function(){
41089         return this.layout;
41090     },
41091     
41092      /**
41093      * Adds a xtype elements to the layout of the nested panel
41094      * <pre><code>
41095
41096 panel.addxtype({
41097        xtype : 'ContentPanel',
41098        region: 'west',
41099        items: [ .... ]
41100    }
41101 );
41102
41103 panel.addxtype({
41104         xtype : 'NestedLayoutPanel',
41105         region: 'west',
41106         layout: {
41107            center: { },
41108            west: { }   
41109         },
41110         items : [ ... list of content panels or nested layout panels.. ]
41111    }
41112 );
41113 </code></pre>
41114      * @param {Object} cfg Xtype definition of item to add.
41115      */
41116     addxtype : function(cfg) {
41117         return this.layout.addxtype(cfg);
41118     
41119     }
41120 });/*
41121  * Based on:
41122  * Ext JS Library 1.1.1
41123  * Copyright(c) 2006-2007, Ext JS, LLC.
41124  *
41125  * Originally Released Under LGPL - original licence link has changed is not relivant.
41126  *
41127  * Fork - LGPL
41128  * <script type="text/javascript">
41129  */
41130 /**
41131  * @class Roo.TabPanel
41132  * @extends Roo.util.Observable
41133  * A lightweight tab container.
41134  * <br><br>
41135  * Usage:
41136  * <pre><code>
41137 // basic tabs 1, built from existing content
41138 var tabs = new Roo.TabPanel("tabs1");
41139 tabs.addTab("script", "View Script");
41140 tabs.addTab("markup", "View Markup");
41141 tabs.activate("script");
41142
41143 // more advanced tabs, built from javascript
41144 var jtabs = new Roo.TabPanel("jtabs");
41145 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41146
41147 // set up the UpdateManager
41148 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41149 var updater = tab2.getUpdateManager();
41150 updater.setDefaultUrl("ajax1.htm");
41151 tab2.on('activate', updater.refresh, updater, true);
41152
41153 // Use setUrl for Ajax loading
41154 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41155 tab3.setUrl("ajax2.htm", null, true);
41156
41157 // Disabled tab
41158 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41159 tab4.disable();
41160
41161 jtabs.activate("jtabs-1");
41162  * </code></pre>
41163  * @constructor
41164  * Create a new TabPanel.
41165  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41166  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41167  */
41168 Roo.bootstrap.panel.Tabs = function(config){
41169     /**
41170     * The container element for this TabPanel.
41171     * @type Roo.Element
41172     */
41173     this.el = Roo.get(config.el);
41174     delete config.el;
41175     if(config){
41176         if(typeof config == "boolean"){
41177             this.tabPosition = config ? "bottom" : "top";
41178         }else{
41179             Roo.apply(this, config);
41180         }
41181     }
41182     
41183     if(this.tabPosition == "bottom"){
41184         // if tabs are at the bottom = create the body first.
41185         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41186         this.el.addClass("roo-tabs-bottom");
41187     }
41188     // next create the tabs holders
41189     
41190     if (this.tabPosition == "west"){
41191         
41192         var reg = this.region; // fake it..
41193         while (reg) {
41194             if (!reg.mgr.parent) {
41195                 break;
41196             }
41197             reg = reg.mgr.parent.region;
41198         }
41199         Roo.log("got nest?");
41200         Roo.log(reg);
41201         if (reg.mgr.getRegion('west')) {
41202             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41203             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41204             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41205             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41206             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41207         
41208             
41209         }
41210         
41211         
41212     } else {
41213      
41214         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41215         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41216         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41217         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41218     }
41219     
41220     
41221     if(Roo.isIE){
41222         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41223     }
41224     
41225     // finally - if tabs are at the top, then create the body last..
41226     if(this.tabPosition != "bottom"){
41227         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41228          * @type Roo.Element
41229          */
41230         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41231         this.el.addClass("roo-tabs-top");
41232     }
41233     this.items = [];
41234
41235     this.bodyEl.setStyle("position", "relative");
41236
41237     this.active = null;
41238     this.activateDelegate = this.activate.createDelegate(this);
41239
41240     this.addEvents({
41241         /**
41242          * @event tabchange
41243          * Fires when the active tab changes
41244          * @param {Roo.TabPanel} this
41245          * @param {Roo.TabPanelItem} activePanel The new active tab
41246          */
41247         "tabchange": true,
41248         /**
41249          * @event beforetabchange
41250          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41251          * @param {Roo.TabPanel} this
41252          * @param {Object} e Set cancel to true on this object to cancel the tab change
41253          * @param {Roo.TabPanelItem} tab The tab being changed to
41254          */
41255         "beforetabchange" : true
41256     });
41257
41258     Roo.EventManager.onWindowResize(this.onResize, this);
41259     this.cpad = this.el.getPadding("lr");
41260     this.hiddenCount = 0;
41261
41262
41263     // toolbar on the tabbar support...
41264     if (this.toolbar) {
41265         alert("no toolbar support yet");
41266         this.toolbar  = false;
41267         /*
41268         var tcfg = this.toolbar;
41269         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41270         this.toolbar = new Roo.Toolbar(tcfg);
41271         if (Roo.isSafari) {
41272             var tbl = tcfg.container.child('table', true);
41273             tbl.setAttribute('width', '100%');
41274         }
41275         */
41276         
41277     }
41278    
41279
41280
41281     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41282 };
41283
41284 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41285     /*
41286      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41287      */
41288     tabPosition : "top",
41289     /*
41290      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41291      */
41292     currentTabWidth : 0,
41293     /*
41294      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41295      */
41296     minTabWidth : 40,
41297     /*
41298      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41299      */
41300     maxTabWidth : 250,
41301     /*
41302      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41303      */
41304     preferredTabWidth : 175,
41305     /*
41306      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41307      */
41308     resizeTabs : false,
41309     /*
41310      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41311      */
41312     monitorResize : true,
41313     /*
41314      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41315      */
41316     toolbar : false,  // set by caller..
41317     
41318     region : false, /// set by caller
41319     
41320     disableTooltips : true, // not used yet...
41321
41322     /**
41323      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41324      * @param {String} id The id of the div to use <b>or create</b>
41325      * @param {String} text The text for the tab
41326      * @param {String} content (optional) Content to put in the TabPanelItem body
41327      * @param {Boolean} closable (optional) True to create a close icon on the tab
41328      * @return {Roo.TabPanelItem} The created TabPanelItem
41329      */
41330     addTab : function(id, text, content, closable, tpl)
41331     {
41332         var item = new Roo.bootstrap.panel.TabItem({
41333             panel: this,
41334             id : id,
41335             text : text,
41336             closable : closable,
41337             tpl : tpl
41338         });
41339         this.addTabItem(item);
41340         if(content){
41341             item.setContent(content);
41342         }
41343         return item;
41344     },
41345
41346     /**
41347      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41348      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41349      * @return {Roo.TabPanelItem}
41350      */
41351     getTab : function(id){
41352         return this.items[id];
41353     },
41354
41355     /**
41356      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41357      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41358      */
41359     hideTab : function(id){
41360         var t = this.items[id];
41361         if(!t.isHidden()){
41362            t.setHidden(true);
41363            this.hiddenCount++;
41364            this.autoSizeTabs();
41365         }
41366     },
41367
41368     /**
41369      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41370      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41371      */
41372     unhideTab : function(id){
41373         var t = this.items[id];
41374         if(t.isHidden()){
41375            t.setHidden(false);
41376            this.hiddenCount--;
41377            this.autoSizeTabs();
41378         }
41379     },
41380
41381     /**
41382      * Adds an existing {@link Roo.TabPanelItem}.
41383      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41384      */
41385     addTabItem : function(item)
41386     {
41387         this.items[item.id] = item;
41388         this.items.push(item);
41389         this.autoSizeTabs();
41390       //  if(this.resizeTabs){
41391     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41392   //         this.autoSizeTabs();
41393 //        }else{
41394 //            item.autoSize();
41395        // }
41396     },
41397
41398     /**
41399      * Removes a {@link Roo.TabPanelItem}.
41400      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41401      */
41402     removeTab : function(id){
41403         var items = this.items;
41404         var tab = items[id];
41405         if(!tab) { return; }
41406         var index = items.indexOf(tab);
41407         if(this.active == tab && items.length > 1){
41408             var newTab = this.getNextAvailable(index);
41409             if(newTab) {
41410                 newTab.activate();
41411             }
41412         }
41413         this.stripEl.dom.removeChild(tab.pnode.dom);
41414         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41415             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41416         }
41417         items.splice(index, 1);
41418         delete this.items[tab.id];
41419         tab.fireEvent("close", tab);
41420         tab.purgeListeners();
41421         this.autoSizeTabs();
41422     },
41423
41424     getNextAvailable : function(start){
41425         var items = this.items;
41426         var index = start;
41427         // look for a next tab that will slide over to
41428         // replace the one being removed
41429         while(index < items.length){
41430             var item = items[++index];
41431             if(item && !item.isHidden()){
41432                 return item;
41433             }
41434         }
41435         // if one isn't found select the previous tab (on the left)
41436         index = start;
41437         while(index >= 0){
41438             var item = items[--index];
41439             if(item && !item.isHidden()){
41440                 return item;
41441             }
41442         }
41443         return null;
41444     },
41445
41446     /**
41447      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41448      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41449      */
41450     disableTab : function(id){
41451         var tab = this.items[id];
41452         if(tab && this.active != tab){
41453             tab.disable();
41454         }
41455     },
41456
41457     /**
41458      * Enables a {@link Roo.TabPanelItem} that is disabled.
41459      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41460      */
41461     enableTab : function(id){
41462         var tab = this.items[id];
41463         tab.enable();
41464     },
41465
41466     /**
41467      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41468      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41469      * @return {Roo.TabPanelItem} The TabPanelItem.
41470      */
41471     activate : function(id)
41472     {
41473         //Roo.log('activite:'  + id);
41474         
41475         var tab = this.items[id];
41476         if(!tab){
41477             return null;
41478         }
41479         if(tab == this.active || tab.disabled){
41480             return tab;
41481         }
41482         var e = {};
41483         this.fireEvent("beforetabchange", this, e, tab);
41484         if(e.cancel !== true && !tab.disabled){
41485             if(this.active){
41486                 this.active.hide();
41487             }
41488             this.active = this.items[id];
41489             this.active.show();
41490             this.fireEvent("tabchange", this, this.active);
41491         }
41492         return tab;
41493     },
41494
41495     /**
41496      * Gets the active {@link Roo.TabPanelItem}.
41497      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41498      */
41499     getActiveTab : function(){
41500         return this.active;
41501     },
41502
41503     /**
41504      * Updates the tab body element to fit the height of the container element
41505      * for overflow scrolling
41506      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41507      */
41508     syncHeight : function(targetHeight){
41509         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41510         var bm = this.bodyEl.getMargins();
41511         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41512         this.bodyEl.setHeight(newHeight);
41513         return newHeight;
41514     },
41515
41516     onResize : function(){
41517         if(this.monitorResize){
41518             this.autoSizeTabs();
41519         }
41520     },
41521
41522     /**
41523      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41524      */
41525     beginUpdate : function(){
41526         this.updating = true;
41527     },
41528
41529     /**
41530      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41531      */
41532     endUpdate : function(){
41533         this.updating = false;
41534         this.autoSizeTabs();
41535     },
41536
41537     /**
41538      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41539      */
41540     autoSizeTabs : function()
41541     {
41542         var count = this.items.length;
41543         var vcount = count - this.hiddenCount;
41544         
41545         if (vcount < 2) {
41546             this.stripEl.hide();
41547         } else {
41548             this.stripEl.show();
41549         }
41550         
41551         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41552             return;
41553         }
41554         
41555         
41556         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41557         var availWidth = Math.floor(w / vcount);
41558         var b = this.stripBody;
41559         if(b.getWidth() > w){
41560             var tabs = this.items;
41561             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41562             if(availWidth < this.minTabWidth){
41563                 /*if(!this.sleft){    // incomplete scrolling code
41564                     this.createScrollButtons();
41565                 }
41566                 this.showScroll();
41567                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41568             }
41569         }else{
41570             if(this.currentTabWidth < this.preferredTabWidth){
41571                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41572             }
41573         }
41574     },
41575
41576     /**
41577      * Returns the number of tabs in this TabPanel.
41578      * @return {Number}
41579      */
41580      getCount : function(){
41581          return this.items.length;
41582      },
41583
41584     /**
41585      * Resizes all the tabs to the passed width
41586      * @param {Number} The new width
41587      */
41588     setTabWidth : function(width){
41589         this.currentTabWidth = width;
41590         for(var i = 0, len = this.items.length; i < len; i++) {
41591                 if(!this.items[i].isHidden()) {
41592                 this.items[i].setWidth(width);
41593             }
41594         }
41595     },
41596
41597     /**
41598      * Destroys this TabPanel
41599      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41600      */
41601     destroy : function(removeEl){
41602         Roo.EventManager.removeResizeListener(this.onResize, this);
41603         for(var i = 0, len = this.items.length; i < len; i++){
41604             this.items[i].purgeListeners();
41605         }
41606         if(removeEl === true){
41607             this.el.update("");
41608             this.el.remove();
41609         }
41610     },
41611     
41612     createStrip : function(container)
41613     {
41614         var strip = document.createElement("nav");
41615         strip.className = Roo.bootstrap.version == 4 ?
41616             "navbar-light bg-light" : 
41617             "navbar navbar-default"; //"x-tabs-wrap";
41618         container.appendChild(strip);
41619         return strip;
41620     },
41621     
41622     createStripList : function(strip)
41623     {
41624         // div wrapper for retard IE
41625         // returns the "tr" element.
41626         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41627         //'<div class="x-tabs-strip-wrap">'+
41628           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41629           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41630         return strip.firstChild; //.firstChild.firstChild.firstChild;
41631     },
41632     createBody : function(container)
41633     {
41634         var body = document.createElement("div");
41635         Roo.id(body, "tab-body");
41636         //Roo.fly(body).addClass("x-tabs-body");
41637         Roo.fly(body).addClass("tab-content");
41638         container.appendChild(body);
41639         return body;
41640     },
41641     createItemBody :function(bodyEl, id){
41642         var body = Roo.getDom(id);
41643         if(!body){
41644             body = document.createElement("div");
41645             body.id = id;
41646         }
41647         //Roo.fly(body).addClass("x-tabs-item-body");
41648         Roo.fly(body).addClass("tab-pane");
41649          bodyEl.insertBefore(body, bodyEl.firstChild);
41650         return body;
41651     },
41652     /** @private */
41653     createStripElements :  function(stripEl, text, closable, tpl)
41654     {
41655         var td = document.createElement("li"); // was td..
41656         td.className = 'nav-item';
41657         
41658         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41659         
41660         
41661         stripEl.appendChild(td);
41662         /*if(closable){
41663             td.className = "x-tabs-closable";
41664             if(!this.closeTpl){
41665                 this.closeTpl = new Roo.Template(
41666                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41667                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41668                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41669                 );
41670             }
41671             var el = this.closeTpl.overwrite(td, {"text": text});
41672             var close = el.getElementsByTagName("div")[0];
41673             var inner = el.getElementsByTagName("em")[0];
41674             return {"el": el, "close": close, "inner": inner};
41675         } else {
41676         */
41677         // not sure what this is..
41678 //            if(!this.tabTpl){
41679                 //this.tabTpl = new Roo.Template(
41680                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41681                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41682                 //);
41683 //                this.tabTpl = new Roo.Template(
41684 //                   '<a href="#">' +
41685 //                   '<span unselectable="on"' +
41686 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41687 //                            ' >{text}</span></a>'
41688 //                );
41689 //                
41690 //            }
41691
41692
41693             var template = tpl || this.tabTpl || false;
41694             
41695             if(!template){
41696                 template =  new Roo.Template(
41697                         Roo.bootstrap.version == 4 ? 
41698                             (
41699                                 '<a class="nav-link" href="#" unselectable="on"' +
41700                                      (this.disableTooltips ? '' : ' title="{text}"') +
41701                                      ' >{text}</a>'
41702                             ) : (
41703                                 '<a class="nav-link" href="#">' +
41704                                 '<span unselectable="on"' +
41705                                          (this.disableTooltips ? '' : ' title="{text}"') +
41706                                     ' >{text}</span></a>'
41707                             )
41708                 );
41709             }
41710             
41711             switch (typeof(template)) {
41712                 case 'object' :
41713                     break;
41714                 case 'string' :
41715                     template = new Roo.Template(template);
41716                     break;
41717                 default :
41718                     break;
41719             }
41720             
41721             var el = template.overwrite(td, {"text": text});
41722             
41723             var inner = el.getElementsByTagName("span")[0];
41724             
41725             return {"el": el, "inner": inner};
41726             
41727     }
41728         
41729     
41730 });
41731
41732 /**
41733  * @class Roo.TabPanelItem
41734  * @extends Roo.util.Observable
41735  * Represents an individual item (tab plus body) in a TabPanel.
41736  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41737  * @param {String} id The id of this TabPanelItem
41738  * @param {String} text The text for the tab of this TabPanelItem
41739  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41740  */
41741 Roo.bootstrap.panel.TabItem = function(config){
41742     /**
41743      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41744      * @type Roo.TabPanel
41745      */
41746     this.tabPanel = config.panel;
41747     /**
41748      * The id for this TabPanelItem
41749      * @type String
41750      */
41751     this.id = config.id;
41752     /** @private */
41753     this.disabled = false;
41754     /** @private */
41755     this.text = config.text;
41756     /** @private */
41757     this.loaded = false;
41758     this.closable = config.closable;
41759
41760     /**
41761      * The body element for this TabPanelItem.
41762      * @type Roo.Element
41763      */
41764     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41765     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41766     this.bodyEl.setStyle("display", "block");
41767     this.bodyEl.setStyle("zoom", "1");
41768     //this.hideAction();
41769
41770     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41771     /** @private */
41772     this.el = Roo.get(els.el);
41773     this.inner = Roo.get(els.inner, true);
41774      this.textEl = Roo.bootstrap.version == 4 ?
41775         this.el : Roo.get(this.el.dom.firstChild, true);
41776
41777     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41778     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41779
41780     
41781 //    this.el.on("mousedown", this.onTabMouseDown, this);
41782     this.el.on("click", this.onTabClick, this);
41783     /** @private */
41784     if(config.closable){
41785         var c = Roo.get(els.close, true);
41786         c.dom.title = this.closeText;
41787         c.addClassOnOver("close-over");
41788         c.on("click", this.closeClick, this);
41789      }
41790
41791     this.addEvents({
41792          /**
41793          * @event activate
41794          * Fires when this tab becomes the active tab.
41795          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41796          * @param {Roo.TabPanelItem} this
41797          */
41798         "activate": true,
41799         /**
41800          * @event beforeclose
41801          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41802          * @param {Roo.TabPanelItem} this
41803          * @param {Object} e Set cancel to true on this object to cancel the close.
41804          */
41805         "beforeclose": true,
41806         /**
41807          * @event close
41808          * Fires when this tab is closed.
41809          * @param {Roo.TabPanelItem} this
41810          */
41811          "close": true,
41812         /**
41813          * @event deactivate
41814          * Fires when this tab is no longer the active tab.
41815          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41816          * @param {Roo.TabPanelItem} this
41817          */
41818          "deactivate" : true
41819     });
41820     this.hidden = false;
41821
41822     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41823 };
41824
41825 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41826            {
41827     purgeListeners : function(){
41828        Roo.util.Observable.prototype.purgeListeners.call(this);
41829        this.el.removeAllListeners();
41830     },
41831     /**
41832      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41833      */
41834     show : function(){
41835         this.status_node.addClass("active");
41836         this.showAction();
41837         if(Roo.isOpera){
41838             this.tabPanel.stripWrap.repaint();
41839         }
41840         this.fireEvent("activate", this.tabPanel, this);
41841     },
41842
41843     /**
41844      * Returns true if this tab is the active tab.
41845      * @return {Boolean}
41846      */
41847     isActive : function(){
41848         return this.tabPanel.getActiveTab() == this;
41849     },
41850
41851     /**
41852      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41853      */
41854     hide : function(){
41855         this.status_node.removeClass("active");
41856         this.hideAction();
41857         this.fireEvent("deactivate", this.tabPanel, this);
41858     },
41859
41860     hideAction : function(){
41861         this.bodyEl.hide();
41862         this.bodyEl.setStyle("position", "absolute");
41863         this.bodyEl.setLeft("-20000px");
41864         this.bodyEl.setTop("-20000px");
41865     },
41866
41867     showAction : function(){
41868         this.bodyEl.setStyle("position", "relative");
41869         this.bodyEl.setTop("");
41870         this.bodyEl.setLeft("");
41871         this.bodyEl.show();
41872     },
41873
41874     /**
41875      * Set the tooltip for the tab.
41876      * @param {String} tooltip The tab's tooltip
41877      */
41878     setTooltip : function(text){
41879         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41880             this.textEl.dom.qtip = text;
41881             this.textEl.dom.removeAttribute('title');
41882         }else{
41883             this.textEl.dom.title = text;
41884         }
41885     },
41886
41887     onTabClick : function(e){
41888         e.preventDefault();
41889         this.tabPanel.activate(this.id);
41890     },
41891
41892     onTabMouseDown : function(e){
41893         e.preventDefault();
41894         this.tabPanel.activate(this.id);
41895     },
41896 /*
41897     getWidth : function(){
41898         return this.inner.getWidth();
41899     },
41900
41901     setWidth : function(width){
41902         var iwidth = width - this.linode.getPadding("lr");
41903         this.inner.setWidth(iwidth);
41904         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41905         this.linode.setWidth(width);
41906     },
41907 */
41908     /**
41909      * Show or hide the tab
41910      * @param {Boolean} hidden True to hide or false to show.
41911      */
41912     setHidden : function(hidden){
41913         this.hidden = hidden;
41914         this.linode.setStyle("display", hidden ? "none" : "");
41915     },
41916
41917     /**
41918      * Returns true if this tab is "hidden"
41919      * @return {Boolean}
41920      */
41921     isHidden : function(){
41922         return this.hidden;
41923     },
41924
41925     /**
41926      * Returns the text for this tab
41927      * @return {String}
41928      */
41929     getText : function(){
41930         return this.text;
41931     },
41932     /*
41933     autoSize : function(){
41934         //this.el.beginMeasure();
41935         this.textEl.setWidth(1);
41936         /*
41937          *  #2804 [new] Tabs in Roojs
41938          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41939          */
41940         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41941         //this.el.endMeasure();
41942     //},
41943
41944     /**
41945      * Sets the text for the tab (Note: this also sets the tooltip text)
41946      * @param {String} text The tab's text and tooltip
41947      */
41948     setText : function(text){
41949         this.text = text;
41950         this.textEl.update(text);
41951         this.setTooltip(text);
41952         //if(!this.tabPanel.resizeTabs){
41953         //    this.autoSize();
41954         //}
41955     },
41956     /**
41957      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41958      */
41959     activate : function(){
41960         this.tabPanel.activate(this.id);
41961     },
41962
41963     /**
41964      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41965      */
41966     disable : function(){
41967         if(this.tabPanel.active != this){
41968             this.disabled = true;
41969             this.status_node.addClass("disabled");
41970         }
41971     },
41972
41973     /**
41974      * Enables this TabPanelItem if it was previously disabled.
41975      */
41976     enable : function(){
41977         this.disabled = false;
41978         this.status_node.removeClass("disabled");
41979     },
41980
41981     /**
41982      * Sets the content for this TabPanelItem.
41983      * @param {String} content The content
41984      * @param {Boolean} loadScripts true to look for and load scripts
41985      */
41986     setContent : function(content, loadScripts){
41987         this.bodyEl.update(content, loadScripts);
41988     },
41989
41990     /**
41991      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41992      * @return {Roo.UpdateManager} The UpdateManager
41993      */
41994     getUpdateManager : function(){
41995         return this.bodyEl.getUpdateManager();
41996     },
41997
41998     /**
41999      * Set a URL to be used to load the content for this TabPanelItem.
42000      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42001      * @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)
42002      * @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)
42003      * @return {Roo.UpdateManager} The UpdateManager
42004      */
42005     setUrl : function(url, params, loadOnce){
42006         if(this.refreshDelegate){
42007             this.un('activate', this.refreshDelegate);
42008         }
42009         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42010         this.on("activate", this.refreshDelegate);
42011         return this.bodyEl.getUpdateManager();
42012     },
42013
42014     /** @private */
42015     _handleRefresh : function(url, params, loadOnce){
42016         if(!loadOnce || !this.loaded){
42017             var updater = this.bodyEl.getUpdateManager();
42018             updater.update(url, params, this._setLoaded.createDelegate(this));
42019         }
42020     },
42021
42022     /**
42023      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42024      *   Will fail silently if the setUrl method has not been called.
42025      *   This does not activate the panel, just updates its content.
42026      */
42027     refresh : function(){
42028         if(this.refreshDelegate){
42029            this.loaded = false;
42030            this.refreshDelegate();
42031         }
42032     },
42033
42034     /** @private */
42035     _setLoaded : function(){
42036         this.loaded = true;
42037     },
42038
42039     /** @private */
42040     closeClick : function(e){
42041         var o = {};
42042         e.stopEvent();
42043         this.fireEvent("beforeclose", this, o);
42044         if(o.cancel !== true){
42045             this.tabPanel.removeTab(this.id);
42046         }
42047     },
42048     /**
42049      * The text displayed in the tooltip for the close icon.
42050      * @type String
42051      */
42052     closeText : "Close this tab"
42053 });
42054 /**
42055 *    This script refer to:
42056 *    Title: International Telephone Input
42057 *    Author: Jack O'Connor
42058 *    Code version:  v12.1.12
42059 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42060 **/
42061
42062 Roo.bootstrap.form.PhoneInputData = function() {
42063     var d = [
42064       [
42065         "Afghanistan (‫افغانستان‬‎)",
42066         "af",
42067         "93"
42068       ],
42069       [
42070         "Albania (Shqipëri)",
42071         "al",
42072         "355"
42073       ],
42074       [
42075         "Algeria (‫الجزائر‬‎)",
42076         "dz",
42077         "213"
42078       ],
42079       [
42080         "American Samoa",
42081         "as",
42082         "1684"
42083       ],
42084       [
42085         "Andorra",
42086         "ad",
42087         "376"
42088       ],
42089       [
42090         "Angola",
42091         "ao",
42092         "244"
42093       ],
42094       [
42095         "Anguilla",
42096         "ai",
42097         "1264"
42098       ],
42099       [
42100         "Antigua and Barbuda",
42101         "ag",
42102         "1268"
42103       ],
42104       [
42105         "Argentina",
42106         "ar",
42107         "54"
42108       ],
42109       [
42110         "Armenia (Հայաստան)",
42111         "am",
42112         "374"
42113       ],
42114       [
42115         "Aruba",
42116         "aw",
42117         "297"
42118       ],
42119       [
42120         "Australia",
42121         "au",
42122         "61",
42123         0
42124       ],
42125       [
42126         "Austria (Österreich)",
42127         "at",
42128         "43"
42129       ],
42130       [
42131         "Azerbaijan (Azərbaycan)",
42132         "az",
42133         "994"
42134       ],
42135       [
42136         "Bahamas",
42137         "bs",
42138         "1242"
42139       ],
42140       [
42141         "Bahrain (‫البحرين‬‎)",
42142         "bh",
42143         "973"
42144       ],
42145       [
42146         "Bangladesh (বাংলাদেশ)",
42147         "bd",
42148         "880"
42149       ],
42150       [
42151         "Barbados",
42152         "bb",
42153         "1246"
42154       ],
42155       [
42156         "Belarus (Беларусь)",
42157         "by",
42158         "375"
42159       ],
42160       [
42161         "Belgium (België)",
42162         "be",
42163         "32"
42164       ],
42165       [
42166         "Belize",
42167         "bz",
42168         "501"
42169       ],
42170       [
42171         "Benin (Bénin)",
42172         "bj",
42173         "229"
42174       ],
42175       [
42176         "Bermuda",
42177         "bm",
42178         "1441"
42179       ],
42180       [
42181         "Bhutan (འབྲུག)",
42182         "bt",
42183         "975"
42184       ],
42185       [
42186         "Bolivia",
42187         "bo",
42188         "591"
42189       ],
42190       [
42191         "Bosnia and Herzegovina (Босна и Херцеговина)",
42192         "ba",
42193         "387"
42194       ],
42195       [
42196         "Botswana",
42197         "bw",
42198         "267"
42199       ],
42200       [
42201         "Brazil (Brasil)",
42202         "br",
42203         "55"
42204       ],
42205       [
42206         "British Indian Ocean Territory",
42207         "io",
42208         "246"
42209       ],
42210       [
42211         "British Virgin Islands",
42212         "vg",
42213         "1284"
42214       ],
42215       [
42216         "Brunei",
42217         "bn",
42218         "673"
42219       ],
42220       [
42221         "Bulgaria (България)",
42222         "bg",
42223         "359"
42224       ],
42225       [
42226         "Burkina Faso",
42227         "bf",
42228         "226"
42229       ],
42230       [
42231         "Burundi (Uburundi)",
42232         "bi",
42233         "257"
42234       ],
42235       [
42236         "Cambodia (កម្ពុជា)",
42237         "kh",
42238         "855"
42239       ],
42240       [
42241         "Cameroon (Cameroun)",
42242         "cm",
42243         "237"
42244       ],
42245       [
42246         "Canada",
42247         "ca",
42248         "1",
42249         1,
42250         ["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"]
42251       ],
42252       [
42253         "Cape Verde (Kabu Verdi)",
42254         "cv",
42255         "238"
42256       ],
42257       [
42258         "Caribbean Netherlands",
42259         "bq",
42260         "599",
42261         1
42262       ],
42263       [
42264         "Cayman Islands",
42265         "ky",
42266         "1345"
42267       ],
42268       [
42269         "Central African Republic (République centrafricaine)",
42270         "cf",
42271         "236"
42272       ],
42273       [
42274         "Chad (Tchad)",
42275         "td",
42276         "235"
42277       ],
42278       [
42279         "Chile",
42280         "cl",
42281         "56"
42282       ],
42283       [
42284         "China (中国)",
42285         "cn",
42286         "86"
42287       ],
42288       [
42289         "Christmas Island",
42290         "cx",
42291         "61",
42292         2
42293       ],
42294       [
42295         "Cocos (Keeling) Islands",
42296         "cc",
42297         "61",
42298         1
42299       ],
42300       [
42301         "Colombia",
42302         "co",
42303         "57"
42304       ],
42305       [
42306         "Comoros (‫جزر القمر‬‎)",
42307         "km",
42308         "269"
42309       ],
42310       [
42311         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42312         "cd",
42313         "243"
42314       ],
42315       [
42316         "Congo (Republic) (Congo-Brazzaville)",
42317         "cg",
42318         "242"
42319       ],
42320       [
42321         "Cook Islands",
42322         "ck",
42323         "682"
42324       ],
42325       [
42326         "Costa Rica",
42327         "cr",
42328         "506"
42329       ],
42330       [
42331         "Côte d’Ivoire",
42332         "ci",
42333         "225"
42334       ],
42335       [
42336         "Croatia (Hrvatska)",
42337         "hr",
42338         "385"
42339       ],
42340       [
42341         "Cuba",
42342         "cu",
42343         "53"
42344       ],
42345       [
42346         "Curaçao",
42347         "cw",
42348         "599",
42349         0
42350       ],
42351       [
42352         "Cyprus (Κύπρος)",
42353         "cy",
42354         "357"
42355       ],
42356       [
42357         "Czech Republic (Česká republika)",
42358         "cz",
42359         "420"
42360       ],
42361       [
42362         "Denmark (Danmark)",
42363         "dk",
42364         "45"
42365       ],
42366       [
42367         "Djibouti",
42368         "dj",
42369         "253"
42370       ],
42371       [
42372         "Dominica",
42373         "dm",
42374         "1767"
42375       ],
42376       [
42377         "Dominican Republic (República Dominicana)",
42378         "do",
42379         "1",
42380         2,
42381         ["809", "829", "849"]
42382       ],
42383       [
42384         "Ecuador",
42385         "ec",
42386         "593"
42387       ],
42388       [
42389         "Egypt (‫مصر‬‎)",
42390         "eg",
42391         "20"
42392       ],
42393       [
42394         "El Salvador",
42395         "sv",
42396         "503"
42397       ],
42398       [
42399         "Equatorial Guinea (Guinea Ecuatorial)",
42400         "gq",
42401         "240"
42402       ],
42403       [
42404         "Eritrea",
42405         "er",
42406         "291"
42407       ],
42408       [
42409         "Estonia (Eesti)",
42410         "ee",
42411         "372"
42412       ],
42413       [
42414         "Ethiopia",
42415         "et",
42416         "251"
42417       ],
42418       [
42419         "Falkland Islands (Islas Malvinas)",
42420         "fk",
42421         "500"
42422       ],
42423       [
42424         "Faroe Islands (Føroyar)",
42425         "fo",
42426         "298"
42427       ],
42428       [
42429         "Fiji",
42430         "fj",
42431         "679"
42432       ],
42433       [
42434         "Finland (Suomi)",
42435         "fi",
42436         "358",
42437         0
42438       ],
42439       [
42440         "France",
42441         "fr",
42442         "33"
42443       ],
42444       [
42445         "French Guiana (Guyane française)",
42446         "gf",
42447         "594"
42448       ],
42449       [
42450         "French Polynesia (Polynésie française)",
42451         "pf",
42452         "689"
42453       ],
42454       [
42455         "Gabon",
42456         "ga",
42457         "241"
42458       ],
42459       [
42460         "Gambia",
42461         "gm",
42462         "220"
42463       ],
42464       [
42465         "Georgia (საქართველო)",
42466         "ge",
42467         "995"
42468       ],
42469       [
42470         "Germany (Deutschland)",
42471         "de",
42472         "49"
42473       ],
42474       [
42475         "Ghana (Gaana)",
42476         "gh",
42477         "233"
42478       ],
42479       [
42480         "Gibraltar",
42481         "gi",
42482         "350"
42483       ],
42484       [
42485         "Greece (Ελλάδα)",
42486         "gr",
42487         "30"
42488       ],
42489       [
42490         "Greenland (Kalaallit Nunaat)",
42491         "gl",
42492         "299"
42493       ],
42494       [
42495         "Grenada",
42496         "gd",
42497         "1473"
42498       ],
42499       [
42500         "Guadeloupe",
42501         "gp",
42502         "590",
42503         0
42504       ],
42505       [
42506         "Guam",
42507         "gu",
42508         "1671"
42509       ],
42510       [
42511         "Guatemala",
42512         "gt",
42513         "502"
42514       ],
42515       [
42516         "Guernsey",
42517         "gg",
42518         "44",
42519         1
42520       ],
42521       [
42522         "Guinea (Guinée)",
42523         "gn",
42524         "224"
42525       ],
42526       [
42527         "Guinea-Bissau (Guiné Bissau)",
42528         "gw",
42529         "245"
42530       ],
42531       [
42532         "Guyana",
42533         "gy",
42534         "592"
42535       ],
42536       [
42537         "Haiti",
42538         "ht",
42539         "509"
42540       ],
42541       [
42542         "Honduras",
42543         "hn",
42544         "504"
42545       ],
42546       [
42547         "Hong Kong (香港)",
42548         "hk",
42549         "852"
42550       ],
42551       [
42552         "Hungary (Magyarország)",
42553         "hu",
42554         "36"
42555       ],
42556       [
42557         "Iceland (Ísland)",
42558         "is",
42559         "354"
42560       ],
42561       [
42562         "India (भारत)",
42563         "in",
42564         "91"
42565       ],
42566       [
42567         "Indonesia",
42568         "id",
42569         "62"
42570       ],
42571       [
42572         "Iran (‫ایران‬‎)",
42573         "ir",
42574         "98"
42575       ],
42576       [
42577         "Iraq (‫العراق‬‎)",
42578         "iq",
42579         "964"
42580       ],
42581       [
42582         "Ireland",
42583         "ie",
42584         "353"
42585       ],
42586       [
42587         "Isle of Man",
42588         "im",
42589         "44",
42590         2
42591       ],
42592       [
42593         "Israel (‫ישראל‬‎)",
42594         "il",
42595         "972"
42596       ],
42597       [
42598         "Italy (Italia)",
42599         "it",
42600         "39",
42601         0
42602       ],
42603       [
42604         "Jamaica",
42605         "jm",
42606         "1876"
42607       ],
42608       [
42609         "Japan (日本)",
42610         "jp",
42611         "81"
42612       ],
42613       [
42614         "Jersey",
42615         "je",
42616         "44",
42617         3
42618       ],
42619       [
42620         "Jordan (‫الأردن‬‎)",
42621         "jo",
42622         "962"
42623       ],
42624       [
42625         "Kazakhstan (Казахстан)",
42626         "kz",
42627         "7",
42628         1
42629       ],
42630       [
42631         "Kenya",
42632         "ke",
42633         "254"
42634       ],
42635       [
42636         "Kiribati",
42637         "ki",
42638         "686"
42639       ],
42640       [
42641         "Kosovo",
42642         "xk",
42643         "383"
42644       ],
42645       [
42646         "Kuwait (‫الكويت‬‎)",
42647         "kw",
42648         "965"
42649       ],
42650       [
42651         "Kyrgyzstan (Кыргызстан)",
42652         "kg",
42653         "996"
42654       ],
42655       [
42656         "Laos (ລາວ)",
42657         "la",
42658         "856"
42659       ],
42660       [
42661         "Latvia (Latvija)",
42662         "lv",
42663         "371"
42664       ],
42665       [
42666         "Lebanon (‫لبنان‬‎)",
42667         "lb",
42668         "961"
42669       ],
42670       [
42671         "Lesotho",
42672         "ls",
42673         "266"
42674       ],
42675       [
42676         "Liberia",
42677         "lr",
42678         "231"
42679       ],
42680       [
42681         "Libya (‫ليبيا‬‎)",
42682         "ly",
42683         "218"
42684       ],
42685       [
42686         "Liechtenstein",
42687         "li",
42688         "423"
42689       ],
42690       [
42691         "Lithuania (Lietuva)",
42692         "lt",
42693         "370"
42694       ],
42695       [
42696         "Luxembourg",
42697         "lu",
42698         "352"
42699       ],
42700       [
42701         "Macau (澳門)",
42702         "mo",
42703         "853"
42704       ],
42705       [
42706         "Macedonia (FYROM) (Македонија)",
42707         "mk",
42708         "389"
42709       ],
42710       [
42711         "Madagascar (Madagasikara)",
42712         "mg",
42713         "261"
42714       ],
42715       [
42716         "Malawi",
42717         "mw",
42718         "265"
42719       ],
42720       [
42721         "Malaysia",
42722         "my",
42723         "60"
42724       ],
42725       [
42726         "Maldives",
42727         "mv",
42728         "960"
42729       ],
42730       [
42731         "Mali",
42732         "ml",
42733         "223"
42734       ],
42735       [
42736         "Malta",
42737         "mt",
42738         "356"
42739       ],
42740       [
42741         "Marshall Islands",
42742         "mh",
42743         "692"
42744       ],
42745       [
42746         "Martinique",
42747         "mq",
42748         "596"
42749       ],
42750       [
42751         "Mauritania (‫موريتانيا‬‎)",
42752         "mr",
42753         "222"
42754       ],
42755       [
42756         "Mauritius (Moris)",
42757         "mu",
42758         "230"
42759       ],
42760       [
42761         "Mayotte",
42762         "yt",
42763         "262",
42764         1
42765       ],
42766       [
42767         "Mexico (México)",
42768         "mx",
42769         "52"
42770       ],
42771       [
42772         "Micronesia",
42773         "fm",
42774         "691"
42775       ],
42776       [
42777         "Moldova (Republica Moldova)",
42778         "md",
42779         "373"
42780       ],
42781       [
42782         "Monaco",
42783         "mc",
42784         "377"
42785       ],
42786       [
42787         "Mongolia (Монгол)",
42788         "mn",
42789         "976"
42790       ],
42791       [
42792         "Montenegro (Crna Gora)",
42793         "me",
42794         "382"
42795       ],
42796       [
42797         "Montserrat",
42798         "ms",
42799         "1664"
42800       ],
42801       [
42802         "Morocco (‫المغرب‬‎)",
42803         "ma",
42804         "212",
42805         0
42806       ],
42807       [
42808         "Mozambique (Moçambique)",
42809         "mz",
42810         "258"
42811       ],
42812       [
42813         "Myanmar (Burma) (မြန်မာ)",
42814         "mm",
42815         "95"
42816       ],
42817       [
42818         "Namibia (Namibië)",
42819         "na",
42820         "264"
42821       ],
42822       [
42823         "Nauru",
42824         "nr",
42825         "674"
42826       ],
42827       [
42828         "Nepal (नेपाल)",
42829         "np",
42830         "977"
42831       ],
42832       [
42833         "Netherlands (Nederland)",
42834         "nl",
42835         "31"
42836       ],
42837       [
42838         "New Caledonia (Nouvelle-Calédonie)",
42839         "nc",
42840         "687"
42841       ],
42842       [
42843         "New Zealand",
42844         "nz",
42845         "64"
42846       ],
42847       [
42848         "Nicaragua",
42849         "ni",
42850         "505"
42851       ],
42852       [
42853         "Niger (Nijar)",
42854         "ne",
42855         "227"
42856       ],
42857       [
42858         "Nigeria",
42859         "ng",
42860         "234"
42861       ],
42862       [
42863         "Niue",
42864         "nu",
42865         "683"
42866       ],
42867       [
42868         "Norfolk Island",
42869         "nf",
42870         "672"
42871       ],
42872       [
42873         "North Korea (조선 민주주의 인민 공화국)",
42874         "kp",
42875         "850"
42876       ],
42877       [
42878         "Northern Mariana Islands",
42879         "mp",
42880         "1670"
42881       ],
42882       [
42883         "Norway (Norge)",
42884         "no",
42885         "47",
42886         0
42887       ],
42888       [
42889         "Oman (‫عُمان‬‎)",
42890         "om",
42891         "968"
42892       ],
42893       [
42894         "Pakistan (‫پاکستان‬‎)",
42895         "pk",
42896         "92"
42897       ],
42898       [
42899         "Palau",
42900         "pw",
42901         "680"
42902       ],
42903       [
42904         "Palestine (‫فلسطين‬‎)",
42905         "ps",
42906         "970"
42907       ],
42908       [
42909         "Panama (Panamá)",
42910         "pa",
42911         "507"
42912       ],
42913       [
42914         "Papua New Guinea",
42915         "pg",
42916         "675"
42917       ],
42918       [
42919         "Paraguay",
42920         "py",
42921         "595"
42922       ],
42923       [
42924         "Peru (Perú)",
42925         "pe",
42926         "51"
42927       ],
42928       [
42929         "Philippines",
42930         "ph",
42931         "63"
42932       ],
42933       [
42934         "Poland (Polska)",
42935         "pl",
42936         "48"
42937       ],
42938       [
42939         "Portugal",
42940         "pt",
42941         "351"
42942       ],
42943       [
42944         "Puerto Rico",
42945         "pr",
42946         "1",
42947         3,
42948         ["787", "939"]
42949       ],
42950       [
42951         "Qatar (‫قطر‬‎)",
42952         "qa",
42953         "974"
42954       ],
42955       [
42956         "Réunion (La Réunion)",
42957         "re",
42958         "262",
42959         0
42960       ],
42961       [
42962         "Romania (România)",
42963         "ro",
42964         "40"
42965       ],
42966       [
42967         "Russia (Россия)",
42968         "ru",
42969         "7",
42970         0
42971       ],
42972       [
42973         "Rwanda",
42974         "rw",
42975         "250"
42976       ],
42977       [
42978         "Saint Barthélemy",
42979         "bl",
42980         "590",
42981         1
42982       ],
42983       [
42984         "Saint Helena",
42985         "sh",
42986         "290"
42987       ],
42988       [
42989         "Saint Kitts and Nevis",
42990         "kn",
42991         "1869"
42992       ],
42993       [
42994         "Saint Lucia",
42995         "lc",
42996         "1758"
42997       ],
42998       [
42999         "Saint Martin (Saint-Martin (partie française))",
43000         "mf",
43001         "590",
43002         2
43003       ],
43004       [
43005         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43006         "pm",
43007         "508"
43008       ],
43009       [
43010         "Saint Vincent and the Grenadines",
43011         "vc",
43012         "1784"
43013       ],
43014       [
43015         "Samoa",
43016         "ws",
43017         "685"
43018       ],
43019       [
43020         "San Marino",
43021         "sm",
43022         "378"
43023       ],
43024       [
43025         "São Tomé and Príncipe (São Tomé e Príncipe)",
43026         "st",
43027         "239"
43028       ],
43029       [
43030         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43031         "sa",
43032         "966"
43033       ],
43034       [
43035         "Senegal (Sénégal)",
43036         "sn",
43037         "221"
43038       ],
43039       [
43040         "Serbia (Србија)",
43041         "rs",
43042         "381"
43043       ],
43044       [
43045         "Seychelles",
43046         "sc",
43047         "248"
43048       ],
43049       [
43050         "Sierra Leone",
43051         "sl",
43052         "232"
43053       ],
43054       [
43055         "Singapore",
43056         "sg",
43057         "65"
43058       ],
43059       [
43060         "Sint Maarten",
43061         "sx",
43062         "1721"
43063       ],
43064       [
43065         "Slovakia (Slovensko)",
43066         "sk",
43067         "421"
43068       ],
43069       [
43070         "Slovenia (Slovenija)",
43071         "si",
43072         "386"
43073       ],
43074       [
43075         "Solomon Islands",
43076         "sb",
43077         "677"
43078       ],
43079       [
43080         "Somalia (Soomaaliya)",
43081         "so",
43082         "252"
43083       ],
43084       [
43085         "South Africa",
43086         "za",
43087         "27"
43088       ],
43089       [
43090         "South Korea (대한민국)",
43091         "kr",
43092         "82"
43093       ],
43094       [
43095         "South Sudan (‫جنوب السودان‬‎)",
43096         "ss",
43097         "211"
43098       ],
43099       [
43100         "Spain (España)",
43101         "es",
43102         "34"
43103       ],
43104       [
43105         "Sri Lanka (ශ්‍රී ලංකාව)",
43106         "lk",
43107         "94"
43108       ],
43109       [
43110         "Sudan (‫السودان‬‎)",
43111         "sd",
43112         "249"
43113       ],
43114       [
43115         "Suriname",
43116         "sr",
43117         "597"
43118       ],
43119       [
43120         "Svalbard and Jan Mayen",
43121         "sj",
43122         "47",
43123         1
43124       ],
43125       [
43126         "Swaziland",
43127         "sz",
43128         "268"
43129       ],
43130       [
43131         "Sweden (Sverige)",
43132         "se",
43133         "46"
43134       ],
43135       [
43136         "Switzerland (Schweiz)",
43137         "ch",
43138         "41"
43139       ],
43140       [
43141         "Syria (‫سوريا‬‎)",
43142         "sy",
43143         "963"
43144       ],
43145       [
43146         "Taiwan (台灣)",
43147         "tw",
43148         "886"
43149       ],
43150       [
43151         "Tajikistan",
43152         "tj",
43153         "992"
43154       ],
43155       [
43156         "Tanzania",
43157         "tz",
43158         "255"
43159       ],
43160       [
43161         "Thailand (ไทย)",
43162         "th",
43163         "66"
43164       ],
43165       [
43166         "Timor-Leste",
43167         "tl",
43168         "670"
43169       ],
43170       [
43171         "Togo",
43172         "tg",
43173         "228"
43174       ],
43175       [
43176         "Tokelau",
43177         "tk",
43178         "690"
43179       ],
43180       [
43181         "Tonga",
43182         "to",
43183         "676"
43184       ],
43185       [
43186         "Trinidad and Tobago",
43187         "tt",
43188         "1868"
43189       ],
43190       [
43191         "Tunisia (‫تونس‬‎)",
43192         "tn",
43193         "216"
43194       ],
43195       [
43196         "Turkey (Türkiye)",
43197         "tr",
43198         "90"
43199       ],
43200       [
43201         "Turkmenistan",
43202         "tm",
43203         "993"
43204       ],
43205       [
43206         "Turks and Caicos Islands",
43207         "tc",
43208         "1649"
43209       ],
43210       [
43211         "Tuvalu",
43212         "tv",
43213         "688"
43214       ],
43215       [
43216         "U.S. Virgin Islands",
43217         "vi",
43218         "1340"
43219       ],
43220       [
43221         "Uganda",
43222         "ug",
43223         "256"
43224       ],
43225       [
43226         "Ukraine (Україна)",
43227         "ua",
43228         "380"
43229       ],
43230       [
43231         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43232         "ae",
43233         "971"
43234       ],
43235       [
43236         "United Kingdom",
43237         "gb",
43238         "44",
43239         0
43240       ],
43241       [
43242         "United States",
43243         "us",
43244         "1",
43245         0
43246       ],
43247       [
43248         "Uruguay",
43249         "uy",
43250         "598"
43251       ],
43252       [
43253         "Uzbekistan (Oʻzbekiston)",
43254         "uz",
43255         "998"
43256       ],
43257       [
43258         "Vanuatu",
43259         "vu",
43260         "678"
43261       ],
43262       [
43263         "Vatican City (Città del Vaticano)",
43264         "va",
43265         "39",
43266         1
43267       ],
43268       [
43269         "Venezuela",
43270         "ve",
43271         "58"
43272       ],
43273       [
43274         "Vietnam (Việt Nam)",
43275         "vn",
43276         "84"
43277       ],
43278       [
43279         "Wallis and Futuna (Wallis-et-Futuna)",
43280         "wf",
43281         "681"
43282       ],
43283       [
43284         "Western Sahara (‫الصحراء الغربية‬‎)",
43285         "eh",
43286         "212",
43287         1
43288       ],
43289       [
43290         "Yemen (‫اليمن‬‎)",
43291         "ye",
43292         "967"
43293       ],
43294       [
43295         "Zambia",
43296         "zm",
43297         "260"
43298       ],
43299       [
43300         "Zimbabwe",
43301         "zw",
43302         "263"
43303       ],
43304       [
43305         "Åland Islands",
43306         "ax",
43307         "358",
43308         1
43309       ]
43310   ];
43311   
43312   return d;
43313 }/**
43314 *    This script refer to:
43315 *    Title: International Telephone Input
43316 *    Author: Jack O'Connor
43317 *    Code version:  v12.1.12
43318 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43319 **/
43320
43321 /**
43322  * @class Roo.bootstrap.form.PhoneInput
43323  * @extends Roo.bootstrap.form.TriggerField
43324  * An input with International dial-code selection
43325  
43326  * @cfg {String} defaultDialCode default '+852'
43327  * @cfg {Array} preferedCountries default []
43328   
43329  * @constructor
43330  * Create a new PhoneInput.
43331  * @param {Object} config Configuration options
43332  */
43333
43334 Roo.bootstrap.form.PhoneInput = function(config) {
43335     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43336 };
43337
43338 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43339         /**
43340         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43341         */
43342         listWidth: undefined,
43343         
43344         selectedClass: 'active',
43345         
43346         invalidClass : "has-warning",
43347         
43348         validClass: 'has-success',
43349         
43350         allowed: '0123456789',
43351         
43352         max_length: 15,
43353         
43354         /**
43355          * @cfg {String} defaultDialCode The default dial code when initializing the input
43356          */
43357         defaultDialCode: '+852',
43358         
43359         /**
43360          * @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
43361          */
43362         preferedCountries: false,
43363         
43364         getAutoCreate : function()
43365         {
43366             var data = Roo.bootstrap.form.PhoneInputData();
43367             var align = this.labelAlign || this.parentLabelAlign();
43368             var id = Roo.id();
43369             
43370             this.allCountries = [];
43371             this.dialCodeMapping = [];
43372             
43373             for (var i = 0; i < data.length; i++) {
43374               var c = data[i];
43375               this.allCountries[i] = {
43376                 name: c[0],
43377                 iso2: c[1],
43378                 dialCode: c[2],
43379                 priority: c[3] || 0,
43380                 areaCodes: c[4] || null
43381               };
43382               this.dialCodeMapping[c[2]] = {
43383                   name: c[0],
43384                   iso2: c[1],
43385                   priority: c[3] || 0,
43386                   areaCodes: c[4] || null
43387               };
43388             }
43389             
43390             var cfg = {
43391                 cls: 'form-group',
43392                 cn: []
43393             };
43394             
43395             var input =  {
43396                 tag: 'input',
43397                 id : id,
43398                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43399                 maxlength: this.max_length,
43400                 cls : 'form-control tel-input',
43401                 autocomplete: 'new-password'
43402             };
43403             
43404             var hiddenInput = {
43405                 tag: 'input',
43406                 type: 'hidden',
43407                 cls: 'hidden-tel-input'
43408             };
43409             
43410             if (this.name) {
43411                 hiddenInput.name = this.name;
43412             }
43413             
43414             if (this.disabled) {
43415                 input.disabled = true;
43416             }
43417             
43418             var flag_container = {
43419                 tag: 'div',
43420                 cls: 'flag-box',
43421                 cn: [
43422                     {
43423                         tag: 'div',
43424                         cls: 'flag'
43425                     },
43426                     {
43427                         tag: 'div',
43428                         cls: 'caret'
43429                     }
43430                 ]
43431             };
43432             
43433             var box = {
43434                 tag: 'div',
43435                 cls: this.hasFeedback ? 'has-feedback' : '',
43436                 cn: [
43437                     hiddenInput,
43438                     input,
43439                     {
43440                         tag: 'input',
43441                         cls: 'dial-code-holder',
43442                         disabled: true
43443                     }
43444                 ]
43445             };
43446             
43447             var container = {
43448                 cls: 'roo-select2-container input-group',
43449                 cn: [
43450                     flag_container,
43451                     box
43452                 ]
43453             };
43454             
43455             if (this.fieldLabel.length) {
43456                 var indicator = {
43457                     tag: 'i',
43458                     tooltip: 'This field is required'
43459                 };
43460                 
43461                 var label = {
43462                     tag: 'label',
43463                     'for':  id,
43464                     cls: 'control-label',
43465                     cn: []
43466                 };
43467                 
43468                 var label_text = {
43469                     tag: 'span',
43470                     html: this.fieldLabel
43471                 };
43472                 
43473                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43474                 label.cn = [
43475                     indicator,
43476                     label_text
43477                 ];
43478                 
43479                 if(this.indicatorpos == 'right') {
43480                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43481                     label.cn = [
43482                         label_text,
43483                         indicator
43484                     ];
43485                 }
43486                 
43487                 if(align == 'left') {
43488                     container = {
43489                         tag: 'div',
43490                         cn: [
43491                             container
43492                         ]
43493                     };
43494                     
43495                     if(this.labelWidth > 12){
43496                         label.style = "width: " + this.labelWidth + 'px';
43497                     }
43498                     if(this.labelWidth < 13 && this.labelmd == 0){
43499                         this.labelmd = this.labelWidth;
43500                     }
43501                     if(this.labellg > 0){
43502                         label.cls += ' col-lg-' + this.labellg;
43503                         input.cls += ' col-lg-' + (12 - this.labellg);
43504                     }
43505                     if(this.labelmd > 0){
43506                         label.cls += ' col-md-' + this.labelmd;
43507                         container.cls += ' col-md-' + (12 - this.labelmd);
43508                     }
43509                     if(this.labelsm > 0){
43510                         label.cls += ' col-sm-' + this.labelsm;
43511                         container.cls += ' col-sm-' + (12 - this.labelsm);
43512                     }
43513                     if(this.labelxs > 0){
43514                         label.cls += ' col-xs-' + this.labelxs;
43515                         container.cls += ' col-xs-' + (12 - this.labelxs);
43516                     }
43517                 }
43518             }
43519             
43520             cfg.cn = [
43521                 label,
43522                 container
43523             ];
43524             
43525             var settings = this;
43526             
43527             ['xs','sm','md','lg'].map(function(size){
43528                 if (settings[size]) {
43529                     cfg.cls += ' col-' + size + '-' + settings[size];
43530                 }
43531             });
43532             
43533             this.store = new Roo.data.Store({
43534                 proxy : new Roo.data.MemoryProxy({}),
43535                 reader : new Roo.data.JsonReader({
43536                     fields : [
43537                         {
43538                             'name' : 'name',
43539                             'type' : 'string'
43540                         },
43541                         {
43542                             'name' : 'iso2',
43543                             'type' : 'string'
43544                         },
43545                         {
43546                             'name' : 'dialCode',
43547                             'type' : 'string'
43548                         },
43549                         {
43550                             'name' : 'priority',
43551                             'type' : 'string'
43552                         },
43553                         {
43554                             'name' : 'areaCodes',
43555                             'type' : 'string'
43556                         }
43557                     ]
43558                 })
43559             });
43560             
43561             if(!this.preferedCountries) {
43562                 this.preferedCountries = [
43563                     'hk',
43564                     'gb',
43565                     'us'
43566                 ];
43567             }
43568             
43569             var p = this.preferedCountries.reverse();
43570             
43571             if(p) {
43572                 for (var i = 0; i < p.length; i++) {
43573                     for (var j = 0; j < this.allCountries.length; j++) {
43574                         if(this.allCountries[j].iso2 == p[i]) {
43575                             var t = this.allCountries[j];
43576                             this.allCountries.splice(j,1);
43577                             this.allCountries.unshift(t);
43578                         }
43579                     } 
43580                 }
43581             }
43582             
43583             this.store.proxy.data = {
43584                 success: true,
43585                 data: this.allCountries
43586             };
43587             
43588             return cfg;
43589         },
43590         
43591         initEvents : function()
43592         {
43593             this.createList();
43594             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43595             
43596             this.indicator = this.indicatorEl();
43597             this.flag = this.flagEl();
43598             this.dialCodeHolder = this.dialCodeHolderEl();
43599             
43600             this.trigger = this.el.select('div.flag-box',true).first();
43601             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43602             
43603             var _this = this;
43604             
43605             (function(){
43606                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43607                 _this.list.setWidth(lw);
43608             }).defer(100);
43609             
43610             this.list.on('mouseover', this.onViewOver, this);
43611             this.list.on('mousemove', this.onViewMove, this);
43612             this.inputEl().on("keyup", this.onKeyUp, this);
43613             this.inputEl().on("keypress", this.onKeyPress, this);
43614             
43615             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43616
43617             this.view = new Roo.View(this.list, this.tpl, {
43618                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43619             });
43620             
43621             this.view.on('click', this.onViewClick, this);
43622             this.setValue(this.defaultDialCode);
43623         },
43624         
43625         onTriggerClick : function(e)
43626         {
43627             Roo.log('trigger click');
43628             if(this.disabled){
43629                 return;
43630             }
43631             
43632             if(this.isExpanded()){
43633                 this.collapse();
43634                 this.hasFocus = false;
43635             }else {
43636                 this.store.load({});
43637                 this.hasFocus = true;
43638                 this.expand();
43639             }
43640         },
43641         
43642         isExpanded : function()
43643         {
43644             return this.list.isVisible();
43645         },
43646         
43647         collapse : function()
43648         {
43649             if(!this.isExpanded()){
43650                 return;
43651             }
43652             this.list.hide();
43653             Roo.get(document).un('mousedown', this.collapseIf, this);
43654             Roo.get(document).un('mousewheel', this.collapseIf, this);
43655             this.fireEvent('collapse', this);
43656             this.validate();
43657         },
43658         
43659         expand : function()
43660         {
43661             Roo.log('expand');
43662
43663             if(this.isExpanded() || !this.hasFocus){
43664                 return;
43665             }
43666             
43667             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43668             this.list.setWidth(lw);
43669             
43670             this.list.show();
43671             this.restrictHeight();
43672             
43673             Roo.get(document).on('mousedown', this.collapseIf, this);
43674             Roo.get(document).on('mousewheel', this.collapseIf, this);
43675             
43676             this.fireEvent('expand', this);
43677         },
43678         
43679         restrictHeight : function()
43680         {
43681             this.list.alignTo(this.inputEl(), this.listAlign);
43682             this.list.alignTo(this.inputEl(), this.listAlign);
43683         },
43684         
43685         onViewOver : function(e, t)
43686         {
43687             if(this.inKeyMode){
43688                 return;
43689             }
43690             var item = this.view.findItemFromChild(t);
43691             
43692             if(item){
43693                 var index = this.view.indexOf(item);
43694                 this.select(index, false);
43695             }
43696         },
43697
43698         // private
43699         onViewClick : function(view, doFocus, el, e)
43700         {
43701             var index = this.view.getSelectedIndexes()[0];
43702             
43703             var r = this.store.getAt(index);
43704             
43705             if(r){
43706                 this.onSelect(r, index);
43707             }
43708             if(doFocus !== false && !this.blockFocus){
43709                 this.inputEl().focus();
43710             }
43711         },
43712         
43713         onViewMove : function(e, t)
43714         {
43715             this.inKeyMode = false;
43716         },
43717         
43718         select : function(index, scrollIntoView)
43719         {
43720             this.selectedIndex = index;
43721             this.view.select(index);
43722             if(scrollIntoView !== false){
43723                 var el = this.view.getNode(index);
43724                 if(el){
43725                     this.list.scrollChildIntoView(el, false);
43726                 }
43727             }
43728         },
43729         
43730         createList : function()
43731         {
43732             this.list = Roo.get(document.body).createChild({
43733                 tag: 'ul',
43734                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43735                 style: 'display:none'
43736             });
43737             
43738             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43739         },
43740         
43741         collapseIf : function(e)
43742         {
43743             var in_combo  = e.within(this.el);
43744             var in_list =  e.within(this.list);
43745             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43746             
43747             if (in_combo || in_list || is_list) {
43748                 return;
43749             }
43750             this.collapse();
43751         },
43752         
43753         onSelect : function(record, index)
43754         {
43755             if(this.fireEvent('beforeselect', this, record, index) !== false){
43756                 
43757                 this.setFlagClass(record.data.iso2);
43758                 this.setDialCode(record.data.dialCode);
43759                 this.hasFocus = false;
43760                 this.collapse();
43761                 this.fireEvent('select', this, record, index);
43762             }
43763         },
43764         
43765         flagEl : function()
43766         {
43767             var flag = this.el.select('div.flag',true).first();
43768             if(!flag){
43769                 return false;
43770             }
43771             return flag;
43772         },
43773         
43774         dialCodeHolderEl : function()
43775         {
43776             var d = this.el.select('input.dial-code-holder',true).first();
43777             if(!d){
43778                 return false;
43779             }
43780             return d;
43781         },
43782         
43783         setDialCode : function(v)
43784         {
43785             this.dialCodeHolder.dom.value = '+'+v;
43786         },
43787         
43788         setFlagClass : function(n)
43789         {
43790             this.flag.dom.className = 'flag '+n;
43791         },
43792         
43793         getValue : function()
43794         {
43795             var v = this.inputEl().getValue();
43796             if(this.dialCodeHolder) {
43797                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43798             }
43799             return v;
43800         },
43801         
43802         setValue : function(v)
43803         {
43804             var d = this.getDialCode(v);
43805             
43806             //invalid dial code
43807             if(v.length == 0 || !d || d.length == 0) {
43808                 if(this.rendered){
43809                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43810                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43811                 }
43812                 return;
43813             }
43814             
43815             //valid dial code
43816             this.setFlagClass(this.dialCodeMapping[d].iso2);
43817             this.setDialCode(d);
43818             this.inputEl().dom.value = v.replace('+'+d,'');
43819             this.hiddenEl().dom.value = this.getValue();
43820             
43821             this.validate();
43822         },
43823         
43824         getDialCode : function(v)
43825         {
43826             v = v ||  '';
43827             
43828             if (v.length == 0) {
43829                 return this.dialCodeHolder.dom.value;
43830             }
43831             
43832             var dialCode = "";
43833             if (v.charAt(0) != "+") {
43834                 return false;
43835             }
43836             var numericChars = "";
43837             for (var i = 1; i < v.length; i++) {
43838               var c = v.charAt(i);
43839               if (!isNaN(c)) {
43840                 numericChars += c;
43841                 if (this.dialCodeMapping[numericChars]) {
43842                   dialCode = v.substr(1, i);
43843                 }
43844                 if (numericChars.length == 4) {
43845                   break;
43846                 }
43847               }
43848             }
43849             return dialCode;
43850         },
43851         
43852         reset : function()
43853         {
43854             this.setValue(this.defaultDialCode);
43855             this.validate();
43856         },
43857         
43858         hiddenEl : function()
43859         {
43860             return this.el.select('input.hidden-tel-input',true).first();
43861         },
43862         
43863         // after setting val
43864         onKeyUp : function(e){
43865             this.setValue(this.getValue());
43866         },
43867         
43868         onKeyPress : function(e){
43869             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43870                 e.stopEvent();
43871             }
43872         }
43873         
43874 });
43875 /**
43876  * @class Roo.bootstrap.form.MoneyField
43877  * @extends Roo.bootstrap.form.ComboBox
43878  * Bootstrap MoneyField class
43879  * 
43880  * @constructor
43881  * Create a new MoneyField.
43882  * @param {Object} config Configuration options
43883  */
43884
43885 Roo.bootstrap.form.MoneyField = function(config) {
43886     
43887     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43888     
43889 };
43890
43891 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43892     
43893     /**
43894      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43895      */
43896     allowDecimals : true,
43897     /**
43898      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43899      */
43900     decimalSeparator : ".",
43901     /**
43902      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43903      */
43904     decimalPrecision : 0,
43905     /**
43906      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43907      */
43908     allowNegative : true,
43909     /**
43910      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43911      */
43912     allowZero: true,
43913     /**
43914      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43915      */
43916     minValue : Number.NEGATIVE_INFINITY,
43917     /**
43918      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43919      */
43920     maxValue : Number.MAX_VALUE,
43921     /**
43922      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43923      */
43924     minText : "The minimum value for this field is {0}",
43925     /**
43926      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43927      */
43928     maxText : "The maximum value for this field is {0}",
43929     /**
43930      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43931      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43932      */
43933     nanText : "{0} is not a valid number",
43934     /**
43935      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43936      */
43937     castInt : true,
43938     /**
43939      * @cfg {String} defaults currency of the MoneyField
43940      * value should be in lkey
43941      */
43942     defaultCurrency : false,
43943     /**
43944      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43945      */
43946     thousandsDelimiter : false,
43947     /**
43948      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43949      */
43950     max_length: false,
43951     
43952     inputlg : 9,
43953     inputmd : 9,
43954     inputsm : 9,
43955     inputxs : 6,
43956      /**
43957      * @cfg {Roo.data.Store} store  Store to lookup currency??
43958      */
43959     store : false,
43960     
43961     getAutoCreate : function()
43962     {
43963         var align = this.labelAlign || this.parentLabelAlign();
43964         
43965         var id = Roo.id();
43966
43967         var cfg = {
43968             cls: 'form-group',
43969             cn: []
43970         };
43971
43972         var input =  {
43973             tag: 'input',
43974             id : id,
43975             cls : 'form-control roo-money-amount-input',
43976             autocomplete: 'new-password'
43977         };
43978         
43979         var hiddenInput = {
43980             tag: 'input',
43981             type: 'hidden',
43982             id: Roo.id(),
43983             cls: 'hidden-number-input'
43984         };
43985         
43986         if(this.max_length) {
43987             input.maxlength = this.max_length; 
43988         }
43989         
43990         if (this.name) {
43991             hiddenInput.name = this.name;
43992         }
43993
43994         if (this.disabled) {
43995             input.disabled = true;
43996         }
43997
43998         var clg = 12 - this.inputlg;
43999         var cmd = 12 - this.inputmd;
44000         var csm = 12 - this.inputsm;
44001         var cxs = 12 - this.inputxs;
44002         
44003         var container = {
44004             tag : 'div',
44005             cls : 'row roo-money-field',
44006             cn : [
44007                 {
44008                     tag : 'div',
44009                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44010                     cn : [
44011                         {
44012                             tag : 'div',
44013                             cls: 'roo-select2-container input-group',
44014                             cn: [
44015                                 {
44016                                     tag : 'input',
44017                                     cls : 'form-control roo-money-currency-input',
44018                                     autocomplete: 'new-password',
44019                                     readOnly : 1,
44020                                     name : this.currencyName
44021                                 },
44022                                 {
44023                                     tag :'span',
44024                                     cls : 'input-group-addon',
44025                                     cn : [
44026                                         {
44027                                             tag: 'span',
44028                                             cls: 'caret'
44029                                         }
44030                                     ]
44031                                 }
44032                             ]
44033                         }
44034                     ]
44035                 },
44036                 {
44037                     tag : 'div',
44038                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44039                     cn : [
44040                         {
44041                             tag: 'div',
44042                             cls: this.hasFeedback ? 'has-feedback' : '',
44043                             cn: [
44044                                 input
44045                             ]
44046                         }
44047                     ]
44048                 }
44049             ]
44050             
44051         };
44052         
44053         if (this.fieldLabel.length) {
44054             var indicator = {
44055                 tag: 'i',
44056                 tooltip: 'This field is required'
44057             };
44058
44059             var label = {
44060                 tag: 'label',
44061                 'for':  id,
44062                 cls: 'control-label',
44063                 cn: []
44064             };
44065
44066             var label_text = {
44067                 tag: 'span',
44068                 html: this.fieldLabel
44069             };
44070
44071             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44072             label.cn = [
44073                 indicator,
44074                 label_text
44075             ];
44076
44077             if(this.indicatorpos == 'right') {
44078                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44079                 label.cn = [
44080                     label_text,
44081                     indicator
44082                 ];
44083             }
44084
44085             if(align == 'left') {
44086                 container = {
44087                     tag: 'div',
44088                     cn: [
44089                         container
44090                     ]
44091                 };
44092
44093                 if(this.labelWidth > 12){
44094                     label.style = "width: " + this.labelWidth + 'px';
44095                 }
44096                 if(this.labelWidth < 13 && this.labelmd == 0){
44097                     this.labelmd = this.labelWidth;
44098                 }
44099                 if(this.labellg > 0){
44100                     label.cls += ' col-lg-' + this.labellg;
44101                     input.cls += ' col-lg-' + (12 - this.labellg);
44102                 }
44103                 if(this.labelmd > 0){
44104                     label.cls += ' col-md-' + this.labelmd;
44105                     container.cls += ' col-md-' + (12 - this.labelmd);
44106                 }
44107                 if(this.labelsm > 0){
44108                     label.cls += ' col-sm-' + this.labelsm;
44109                     container.cls += ' col-sm-' + (12 - this.labelsm);
44110                 }
44111                 if(this.labelxs > 0){
44112                     label.cls += ' col-xs-' + this.labelxs;
44113                     container.cls += ' col-xs-' + (12 - this.labelxs);
44114                 }
44115             }
44116         }
44117
44118         cfg.cn = [
44119             label,
44120             container,
44121             hiddenInput
44122         ];
44123         
44124         var settings = this;
44125
44126         ['xs','sm','md','lg'].map(function(size){
44127             if (settings[size]) {
44128                 cfg.cls += ' col-' + size + '-' + settings[size];
44129             }
44130         });
44131         
44132         return cfg;
44133     },
44134     
44135     initEvents : function()
44136     {
44137         this.indicator = this.indicatorEl();
44138         
44139         this.initCurrencyEvent();
44140         
44141         this.initNumberEvent();
44142     },
44143     
44144     initCurrencyEvent : function()
44145     {
44146         if (!this.store) {
44147             throw "can not find store for combo";
44148         }
44149         
44150         this.store = Roo.factory(this.store, Roo.data);
44151         this.store.parent = this;
44152         
44153         this.createList();
44154         
44155         this.triggerEl = this.el.select('.input-group-addon', true).first();
44156         
44157         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44158         
44159         var _this = this;
44160         
44161         (function(){
44162             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44163             _this.list.setWidth(lw);
44164         }).defer(100);
44165         
44166         this.list.on('mouseover', this.onViewOver, this);
44167         this.list.on('mousemove', this.onViewMove, this);
44168         this.list.on('scroll', this.onViewScroll, this);
44169         
44170         if(!this.tpl){
44171             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44172         }
44173         
44174         this.view = new Roo.View(this.list, this.tpl, {
44175             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44176         });
44177         
44178         this.view.on('click', this.onViewClick, this);
44179         
44180         this.store.on('beforeload', this.onBeforeLoad, this);
44181         this.store.on('load', this.onLoad, this);
44182         this.store.on('loadexception', this.onLoadException, this);
44183         
44184         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44185             "up" : function(e){
44186                 this.inKeyMode = true;
44187                 this.selectPrev();
44188             },
44189
44190             "down" : function(e){
44191                 if(!this.isExpanded()){
44192                     this.onTriggerClick();
44193                 }else{
44194                     this.inKeyMode = true;
44195                     this.selectNext();
44196                 }
44197             },
44198
44199             "enter" : function(e){
44200                 this.collapse();
44201                 
44202                 if(this.fireEvent("specialkey", this, e)){
44203                     this.onViewClick(false);
44204                 }
44205                 
44206                 return true;
44207             },
44208
44209             "esc" : function(e){
44210                 this.collapse();
44211             },
44212
44213             "tab" : function(e){
44214                 this.collapse();
44215                 
44216                 if(this.fireEvent("specialkey", this, e)){
44217                     this.onViewClick(false);
44218                 }
44219                 
44220                 return true;
44221             },
44222
44223             scope : this,
44224
44225             doRelay : function(foo, bar, hname){
44226                 if(hname == 'down' || this.scope.isExpanded()){
44227                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44228                 }
44229                 return true;
44230             },
44231
44232             forceKeyDown: true
44233         });
44234         
44235         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44236         
44237     },
44238     
44239     initNumberEvent : function(e)
44240     {
44241         this.inputEl().on("keydown" , this.fireKey,  this);
44242         this.inputEl().on("focus", this.onFocus,  this);
44243         this.inputEl().on("blur", this.onBlur,  this);
44244         
44245         this.inputEl().relayEvent('keyup', this);
44246         
44247         if(this.indicator){
44248             this.indicator.addClass('invisible');
44249         }
44250  
44251         this.originalValue = this.getValue();
44252         
44253         if(this.validationEvent == 'keyup'){
44254             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44255             this.inputEl().on('keyup', this.filterValidation, this);
44256         }
44257         else if(this.validationEvent !== false){
44258             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44259         }
44260         
44261         if(this.selectOnFocus){
44262             this.on("focus", this.preFocus, this);
44263             
44264         }
44265         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44266             this.inputEl().on("keypress", this.filterKeys, this);
44267         } else {
44268             this.inputEl().relayEvent('keypress', this);
44269         }
44270         
44271         var allowed = "0123456789";
44272         
44273         if(this.allowDecimals){
44274             allowed += this.decimalSeparator;
44275         }
44276         
44277         if(this.allowNegative){
44278             allowed += "-";
44279         }
44280         
44281         if(this.thousandsDelimiter) {
44282             allowed += ",";
44283         }
44284         
44285         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44286         
44287         var keyPress = function(e){
44288             
44289             var k = e.getKey();
44290             
44291             var c = e.getCharCode();
44292             
44293             if(
44294                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44295                     allowed.indexOf(String.fromCharCode(c)) === -1
44296             ){
44297                 e.stopEvent();
44298                 return;
44299             }
44300             
44301             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44302                 return;
44303             }
44304             
44305             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44306                 e.stopEvent();
44307             }
44308         };
44309         
44310         this.inputEl().on("keypress", keyPress, this);
44311         
44312     },
44313     
44314     onTriggerClick : function(e)
44315     {   
44316         if(this.disabled){
44317             return;
44318         }
44319         
44320         this.page = 0;
44321         this.loadNext = false;
44322         
44323         if(this.isExpanded()){
44324             this.collapse();
44325             return;
44326         }
44327         
44328         this.hasFocus = true;
44329         
44330         if(this.triggerAction == 'all') {
44331             this.doQuery(this.allQuery, true);
44332             return;
44333         }
44334         
44335         this.doQuery(this.getRawValue());
44336     },
44337     
44338     getCurrency : function()
44339     {   
44340         var v = this.currencyEl().getValue();
44341         
44342         return v;
44343     },
44344     
44345     restrictHeight : function()
44346     {
44347         this.list.alignTo(this.currencyEl(), this.listAlign);
44348         this.list.alignTo(this.currencyEl(), this.listAlign);
44349     },
44350     
44351     onViewClick : function(view, doFocus, el, e)
44352     {
44353         var index = this.view.getSelectedIndexes()[0];
44354         
44355         var r = this.store.getAt(index);
44356         
44357         if(r){
44358             this.onSelect(r, index);
44359         }
44360     },
44361     
44362     onSelect : function(record, index){
44363         
44364         if(this.fireEvent('beforeselect', this, record, index) !== false){
44365         
44366             this.setFromCurrencyData(index > -1 ? record.data : false);
44367             
44368             this.collapse();
44369             
44370             this.fireEvent('select', this, record, index);
44371         }
44372     },
44373     
44374     setFromCurrencyData : function(o)
44375     {
44376         var currency = '';
44377         
44378         this.lastCurrency = o;
44379         
44380         if (this.currencyField) {
44381             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44382         } else {
44383             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44384         }
44385         
44386         this.lastSelectionText = currency;
44387         
44388         //setting default currency
44389         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44390             this.setCurrency(this.defaultCurrency);
44391             return;
44392         }
44393         
44394         this.setCurrency(currency);
44395     },
44396     
44397     setFromData : function(o)
44398     {
44399         var c = {};
44400         
44401         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44402         
44403         this.setFromCurrencyData(c);
44404         
44405         var value = '';
44406         
44407         if (this.name) {
44408             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44409         } else {
44410             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44411         }
44412         
44413         this.setValue(value);
44414         
44415     },
44416     
44417     setCurrency : function(v)
44418     {   
44419         this.currencyValue = v;
44420         
44421         if(this.rendered){
44422             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44423             this.validate();
44424         }
44425     },
44426     
44427     setValue : function(v)
44428     {
44429         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44430         
44431         this.value = v;
44432         
44433         if(this.rendered){
44434             
44435             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44436             
44437             this.inputEl().dom.value = (v == '') ? '' :
44438                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44439             
44440             if(!this.allowZero && v === '0') {
44441                 this.hiddenEl().dom.value = '';
44442                 this.inputEl().dom.value = '';
44443             }
44444             
44445             this.validate();
44446         }
44447     },
44448     
44449     getRawValue : function()
44450     {
44451         var v = this.inputEl().getValue();
44452         
44453         return v;
44454     },
44455     
44456     getValue : function()
44457     {
44458         return this.fixPrecision(this.parseValue(this.getRawValue()));
44459     },
44460     
44461     parseValue : function(value)
44462     {
44463         if(this.thousandsDelimiter) {
44464             value += "";
44465             r = new RegExp(",", "g");
44466             value = value.replace(r, "");
44467         }
44468         
44469         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44470         return isNaN(value) ? '' : value;
44471         
44472     },
44473     
44474     fixPrecision : function(value)
44475     {
44476         if(this.thousandsDelimiter) {
44477             value += "";
44478             r = new RegExp(",", "g");
44479             value = value.replace(r, "");
44480         }
44481         
44482         var nan = isNaN(value);
44483         
44484         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44485             return nan ? '' : value;
44486         }
44487         return parseFloat(value).toFixed(this.decimalPrecision);
44488     },
44489     
44490     decimalPrecisionFcn : function(v)
44491     {
44492         return Math.floor(v);
44493     },
44494     
44495     validateValue : function(value)
44496     {
44497         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44498             return false;
44499         }
44500         
44501         var num = this.parseValue(value);
44502         
44503         if(isNaN(num)){
44504             this.markInvalid(String.format(this.nanText, value));
44505             return false;
44506         }
44507         
44508         if(num < this.minValue){
44509             this.markInvalid(String.format(this.minText, this.minValue));
44510             return false;
44511         }
44512         
44513         if(num > this.maxValue){
44514             this.markInvalid(String.format(this.maxText, this.maxValue));
44515             return false;
44516         }
44517         
44518         return true;
44519     },
44520     
44521     validate : function()
44522     {
44523         if(this.disabled || this.allowBlank){
44524             this.markValid();
44525             return true;
44526         }
44527         
44528         var currency = this.getCurrency();
44529         
44530         if(this.validateValue(this.getRawValue()) && currency.length){
44531             this.markValid();
44532             return true;
44533         }
44534         
44535         this.markInvalid();
44536         return false;
44537     },
44538     
44539     getName: function()
44540     {
44541         return this.name;
44542     },
44543     
44544     beforeBlur : function()
44545     {
44546         if(!this.castInt){
44547             return;
44548         }
44549         
44550         var v = this.parseValue(this.getRawValue());
44551         
44552         if(v || v == 0){
44553             this.setValue(v);
44554         }
44555     },
44556     
44557     onBlur : function()
44558     {
44559         this.beforeBlur();
44560         
44561         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44562             //this.el.removeClass(this.focusClass);
44563         }
44564         
44565         this.hasFocus = false;
44566         
44567         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44568             this.validate();
44569         }
44570         
44571         var v = this.getValue();
44572         
44573         if(String(v) !== String(this.startValue)){
44574             this.fireEvent('change', this, v, this.startValue);
44575         }
44576         
44577         this.fireEvent("blur", this);
44578     },
44579     
44580     inputEl : function()
44581     {
44582         return this.el.select('.roo-money-amount-input', true).first();
44583     },
44584     
44585     currencyEl : function()
44586     {
44587         return this.el.select('.roo-money-currency-input', true).first();
44588     },
44589     
44590     hiddenEl : function()
44591     {
44592         return this.el.select('input.hidden-number-input',true).first();
44593     }
44594     
44595 });/**
44596  * @class Roo.bootstrap.BezierSignature
44597  * @extends Roo.bootstrap.Component
44598  * Bootstrap BezierSignature class
44599  * This script refer to:
44600  *    Title: Signature Pad
44601  *    Author: szimek
44602  *    Availability: https://github.com/szimek/signature_pad
44603  *
44604  * @constructor
44605  * Create a new BezierSignature
44606  * @param {Object} config The config object
44607  */
44608
44609 Roo.bootstrap.BezierSignature = function(config){
44610     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44611     this.addEvents({
44612         "resize" : true
44613     });
44614 };
44615
44616 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44617 {
44618      
44619     curve_data: [],
44620     
44621     is_empty: true,
44622     
44623     mouse_btn_down: true,
44624     
44625     /**
44626      * @cfg {int} canvas height
44627      */
44628     canvas_height: '200px',
44629     
44630     /**
44631      * @cfg {float|function} Radius of a single dot.
44632      */ 
44633     dot_size: false,
44634     
44635     /**
44636      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44637      */
44638     min_width: 0.5,
44639     
44640     /**
44641      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44642      */
44643     max_width: 2.5,
44644     
44645     /**
44646      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44647      */
44648     throttle: 16,
44649     
44650     /**
44651      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44652      */
44653     min_distance: 5,
44654     
44655     /**
44656      * @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.
44657      */
44658     bg_color: 'rgba(0, 0, 0, 0)',
44659     
44660     /**
44661      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44662      */
44663     dot_color: 'black',
44664     
44665     /**
44666      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44667      */ 
44668     velocity_filter_weight: 0.7,
44669     
44670     /**
44671      * @cfg {function} Callback when stroke begin. 
44672      */
44673     onBegin: false,
44674     
44675     /**
44676      * @cfg {function} Callback when stroke end.
44677      */
44678     onEnd: false,
44679     
44680     getAutoCreate : function()
44681     {
44682         var cls = 'roo-signature column';
44683         
44684         if(this.cls){
44685             cls += ' ' + this.cls;
44686         }
44687         
44688         var col_sizes = [
44689             'lg',
44690             'md',
44691             'sm',
44692             'xs'
44693         ];
44694         
44695         for(var i = 0; i < col_sizes.length; i++) {
44696             if(this[col_sizes[i]]) {
44697                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44698             }
44699         }
44700         
44701         var cfg = {
44702             tag: 'div',
44703             cls: cls,
44704             cn: [
44705                 {
44706                     tag: 'div',
44707                     cls: 'roo-signature-body',
44708                     cn: [
44709                         {
44710                             tag: 'canvas',
44711                             cls: 'roo-signature-body-canvas',
44712                             height: this.canvas_height,
44713                             width: this.canvas_width
44714                         }
44715                     ]
44716                 },
44717                 {
44718                     tag: 'input',
44719                     type: 'file',
44720                     style: 'display: none'
44721                 }
44722             ]
44723         };
44724         
44725         return cfg;
44726     },
44727     
44728     initEvents: function() 
44729     {
44730         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44731         
44732         var canvas = this.canvasEl();
44733         
44734         // mouse && touch event swapping...
44735         canvas.dom.style.touchAction = 'none';
44736         canvas.dom.style.msTouchAction = 'none';
44737         
44738         this.mouse_btn_down = false;
44739         canvas.on('mousedown', this._handleMouseDown, this);
44740         canvas.on('mousemove', this._handleMouseMove, this);
44741         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44742         
44743         if (window.PointerEvent) {
44744             canvas.on('pointerdown', this._handleMouseDown, this);
44745             canvas.on('pointermove', this._handleMouseMove, this);
44746             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44747         }
44748         
44749         if ('ontouchstart' in window) {
44750             canvas.on('touchstart', this._handleTouchStart, this);
44751             canvas.on('touchmove', this._handleTouchMove, this);
44752             canvas.on('touchend', this._handleTouchEnd, this);
44753         }
44754         
44755         Roo.EventManager.onWindowResize(this.resize, this, true);
44756         
44757         // file input event
44758         this.fileEl().on('change', this.uploadImage, this);
44759         
44760         this.clear();
44761         
44762         this.resize();
44763     },
44764     
44765     resize: function(){
44766         
44767         var canvas = this.canvasEl().dom;
44768         var ctx = this.canvasElCtx();
44769         var img_data = false;
44770         
44771         if(canvas.width > 0) {
44772             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44773         }
44774         // setting canvas width will clean img data
44775         canvas.width = 0;
44776         
44777         var style = window.getComputedStyle ? 
44778             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44779             
44780         var padding_left = parseInt(style.paddingLeft) || 0;
44781         var padding_right = parseInt(style.paddingRight) || 0;
44782         
44783         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44784         
44785         if(img_data) {
44786             ctx.putImageData(img_data, 0, 0);
44787         }
44788     },
44789     
44790     _handleMouseDown: function(e)
44791     {
44792         if (e.browserEvent.which === 1) {
44793             this.mouse_btn_down = true;
44794             this.strokeBegin(e);
44795         }
44796     },
44797     
44798     _handleMouseMove: function (e)
44799     {
44800         if (this.mouse_btn_down) {
44801             this.strokeMoveUpdate(e);
44802         }
44803     },
44804     
44805     _handleMouseUp: function (e)
44806     {
44807         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44808             this.mouse_btn_down = false;
44809             this.strokeEnd(e);
44810         }
44811     },
44812     
44813     _handleTouchStart: function (e) {
44814         
44815         e.preventDefault();
44816         if (e.browserEvent.targetTouches.length === 1) {
44817             // var touch = e.browserEvent.changedTouches[0];
44818             // this.strokeBegin(touch);
44819             
44820              this.strokeBegin(e); // assume e catching the correct xy...
44821         }
44822     },
44823     
44824     _handleTouchMove: function (e) {
44825         e.preventDefault();
44826         // var touch = event.targetTouches[0];
44827         // _this._strokeMoveUpdate(touch);
44828         this.strokeMoveUpdate(e);
44829     },
44830     
44831     _handleTouchEnd: function (e) {
44832         var wasCanvasTouched = e.target === this.canvasEl().dom;
44833         if (wasCanvasTouched) {
44834             e.preventDefault();
44835             // var touch = event.changedTouches[0];
44836             // _this._strokeEnd(touch);
44837             this.strokeEnd(e);
44838         }
44839     },
44840     
44841     reset: function () {
44842         this._lastPoints = [];
44843         this._lastVelocity = 0;
44844         this._lastWidth = (this.min_width + this.max_width) / 2;
44845         this.canvasElCtx().fillStyle = this.dot_color;
44846     },
44847     
44848     strokeMoveUpdate: function(e)
44849     {
44850         this.strokeUpdate(e);
44851         
44852         if (this.throttle) {
44853             this.throttleStroke(this.strokeUpdate, this.throttle);
44854         }
44855         else {
44856             this.strokeUpdate(e);
44857         }
44858     },
44859     
44860     strokeBegin: function(e)
44861     {
44862         var newPointGroup = {
44863             color: this.dot_color,
44864             points: []
44865         };
44866         
44867         if (typeof this.onBegin === 'function') {
44868             this.onBegin(e);
44869         }
44870         
44871         this.curve_data.push(newPointGroup);
44872         this.reset();
44873         this.strokeUpdate(e);
44874     },
44875     
44876     strokeUpdate: function(e)
44877     {
44878         var rect = this.canvasEl().dom.getBoundingClientRect();
44879         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44880         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44881         var lastPoints = lastPointGroup.points;
44882         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44883         var isLastPointTooClose = lastPoint
44884             ? point.distanceTo(lastPoint) <= this.min_distance
44885             : false;
44886         var color = lastPointGroup.color;
44887         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44888             var curve = this.addPoint(point);
44889             if (!lastPoint) {
44890                 this.drawDot({color: color, point: point});
44891             }
44892             else if (curve) {
44893                 this.drawCurve({color: color, curve: curve});
44894             }
44895             lastPoints.push({
44896                 time: point.time,
44897                 x: point.x,
44898                 y: point.y
44899             });
44900         }
44901     },
44902     
44903     strokeEnd: function(e)
44904     {
44905         this.strokeUpdate(e);
44906         if (typeof this.onEnd === 'function') {
44907             this.onEnd(e);
44908         }
44909     },
44910     
44911     addPoint:  function (point) {
44912         var _lastPoints = this._lastPoints;
44913         _lastPoints.push(point);
44914         if (_lastPoints.length > 2) {
44915             if (_lastPoints.length === 3) {
44916                 _lastPoints.unshift(_lastPoints[0]);
44917             }
44918             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44919             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44920             _lastPoints.shift();
44921             return curve;
44922         }
44923         return null;
44924     },
44925     
44926     calculateCurveWidths: function (startPoint, endPoint) {
44927         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44928             (1 - this.velocity_filter_weight) * this._lastVelocity;
44929
44930         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44931         var widths = {
44932             end: newWidth,
44933             start: this._lastWidth
44934         };
44935         
44936         this._lastVelocity = velocity;
44937         this._lastWidth = newWidth;
44938         return widths;
44939     },
44940     
44941     drawDot: function (_a) {
44942         var color = _a.color, point = _a.point;
44943         var ctx = this.canvasElCtx();
44944         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44945         ctx.beginPath();
44946         this.drawCurveSegment(point.x, point.y, width);
44947         ctx.closePath();
44948         ctx.fillStyle = color;
44949         ctx.fill();
44950     },
44951     
44952     drawCurve: function (_a) {
44953         var color = _a.color, curve = _a.curve;
44954         var ctx = this.canvasElCtx();
44955         var widthDelta = curve.endWidth - curve.startWidth;
44956         var drawSteps = Math.floor(curve.length()) * 2;
44957         ctx.beginPath();
44958         ctx.fillStyle = color;
44959         for (var i = 0; i < drawSteps; i += 1) {
44960         var t = i / drawSteps;
44961         var tt = t * t;
44962         var ttt = tt * t;
44963         var u = 1 - t;
44964         var uu = u * u;
44965         var uuu = uu * u;
44966         var x = uuu * curve.startPoint.x;
44967         x += 3 * uu * t * curve.control1.x;
44968         x += 3 * u * tt * curve.control2.x;
44969         x += ttt * curve.endPoint.x;
44970         var y = uuu * curve.startPoint.y;
44971         y += 3 * uu * t * curve.control1.y;
44972         y += 3 * u * tt * curve.control2.y;
44973         y += ttt * curve.endPoint.y;
44974         var width = curve.startWidth + ttt * widthDelta;
44975         this.drawCurveSegment(x, y, width);
44976         }
44977         ctx.closePath();
44978         ctx.fill();
44979     },
44980     
44981     drawCurveSegment: function (x, y, width) {
44982         var ctx = this.canvasElCtx();
44983         ctx.moveTo(x, y);
44984         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44985         this.is_empty = false;
44986     },
44987     
44988     clear: function()
44989     {
44990         var ctx = this.canvasElCtx();
44991         var canvas = this.canvasEl().dom;
44992         ctx.fillStyle = this.bg_color;
44993         ctx.clearRect(0, 0, canvas.width, canvas.height);
44994         ctx.fillRect(0, 0, canvas.width, canvas.height);
44995         this.curve_data = [];
44996         this.reset();
44997         this.is_empty = true;
44998     },
44999     
45000     fileEl: function()
45001     {
45002         return  this.el.select('input',true).first();
45003     },
45004     
45005     canvasEl: function()
45006     {
45007         return this.el.select('canvas',true).first();
45008     },
45009     
45010     canvasElCtx: function()
45011     {
45012         return this.el.select('canvas',true).first().dom.getContext('2d');
45013     },
45014     
45015     getImage: function(type)
45016     {
45017         if(this.is_empty) {
45018             return false;
45019         }
45020         
45021         // encryption ?
45022         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45023     },
45024     
45025     drawFromImage: function(img_src)
45026     {
45027         var img = new Image();
45028         
45029         img.onload = function(){
45030             this.canvasElCtx().drawImage(img, 0, 0);
45031         }.bind(this);
45032         
45033         img.src = img_src;
45034         
45035         this.is_empty = false;
45036     },
45037     
45038     selectImage: function()
45039     {
45040         this.fileEl().dom.click();
45041     },
45042     
45043     uploadImage: function(e)
45044     {
45045         var reader = new FileReader();
45046         
45047         reader.onload = function(e){
45048             var img = new Image();
45049             img.onload = function(){
45050                 this.reset();
45051                 this.canvasElCtx().drawImage(img, 0, 0);
45052             }.bind(this);
45053             img.src = e.target.result;
45054         }.bind(this);
45055         
45056         reader.readAsDataURL(e.target.files[0]);
45057     },
45058     
45059     // Bezier Point Constructor
45060     Point: (function () {
45061         function Point(x, y, time) {
45062             this.x = x;
45063             this.y = y;
45064             this.time = time || Date.now();
45065         }
45066         Point.prototype.distanceTo = function (start) {
45067             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45068         };
45069         Point.prototype.equals = function (other) {
45070             return this.x === other.x && this.y === other.y && this.time === other.time;
45071         };
45072         Point.prototype.velocityFrom = function (start) {
45073             return this.time !== start.time
45074             ? this.distanceTo(start) / (this.time - start.time)
45075             : 0;
45076         };
45077         return Point;
45078     }()),
45079     
45080     
45081     // Bezier Constructor
45082     Bezier: (function () {
45083         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45084             this.startPoint = startPoint;
45085             this.control2 = control2;
45086             this.control1 = control1;
45087             this.endPoint = endPoint;
45088             this.startWidth = startWidth;
45089             this.endWidth = endWidth;
45090         }
45091         Bezier.fromPoints = function (points, widths, scope) {
45092             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45093             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45094             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45095         };
45096         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45097             var dx1 = s1.x - s2.x;
45098             var dy1 = s1.y - s2.y;
45099             var dx2 = s2.x - s3.x;
45100             var dy2 = s2.y - s3.y;
45101             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45102             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45103             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45104             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45105             var dxm = m1.x - m2.x;
45106             var dym = m1.y - m2.y;
45107             var k = l2 / (l1 + l2);
45108             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45109             var tx = s2.x - cm.x;
45110             var ty = s2.y - cm.y;
45111             return {
45112                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45113                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45114             };
45115         };
45116         Bezier.prototype.length = function () {
45117             var steps = 10;
45118             var length = 0;
45119             var px;
45120             var py;
45121             for (var i = 0; i <= steps; i += 1) {
45122                 var t = i / steps;
45123                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45124                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45125                 if (i > 0) {
45126                     var xdiff = cx - px;
45127                     var ydiff = cy - py;
45128                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45129                 }
45130                 px = cx;
45131                 py = cy;
45132             }
45133             return length;
45134         };
45135         Bezier.prototype.point = function (t, start, c1, c2, end) {
45136             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45137             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45138             + (3.0 * c2 * (1.0 - t) * t * t)
45139             + (end * t * t * t);
45140         };
45141         return Bezier;
45142     }()),
45143     
45144     throttleStroke: function(fn, wait) {
45145       if (wait === void 0) { wait = 250; }
45146       var previous = 0;
45147       var timeout = null;
45148       var result;
45149       var storedContext;
45150       var storedArgs;
45151       var later = function () {
45152           previous = Date.now();
45153           timeout = null;
45154           result = fn.apply(storedContext, storedArgs);
45155           if (!timeout) {
45156               storedContext = null;
45157               storedArgs = [];
45158           }
45159       };
45160       return function wrapper() {
45161           var args = [];
45162           for (var _i = 0; _i < arguments.length; _i++) {
45163               args[_i] = arguments[_i];
45164           }
45165           var now = Date.now();
45166           var remaining = wait - (now - previous);
45167           storedContext = this;
45168           storedArgs = args;
45169           if (remaining <= 0 || remaining > wait) {
45170               if (timeout) {
45171                   clearTimeout(timeout);
45172                   timeout = null;
45173               }
45174               previous = now;
45175               result = fn.apply(storedContext, storedArgs);
45176               if (!timeout) {
45177                   storedContext = null;
45178                   storedArgs = [];
45179               }
45180           }
45181           else if (!timeout) {
45182               timeout = window.setTimeout(later, remaining);
45183           }
45184           return result;
45185       };
45186   }
45187   
45188 });
45189
45190  
45191
45192  // old names for form elements
45193 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
45194 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
45195 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
45196 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
45197 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
45198 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
45199 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
45200 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
45201 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
45202 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
45203 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
45204 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
45205 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
45206 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
45207 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
45208 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
45209 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
45210 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
45211 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
45212 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
45213 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
45214 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
45215 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
45216 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
45217 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
45218 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
45219
45220 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
45221 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45222
45223 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
45224 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
45225
45226 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
45227 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45228 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
45229 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
45230