fix menu children
[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 Roo.bootstrap.menu.Separator
3620  * @parent none
3621  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3622  * 
3623  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3624  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3625  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3626  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3627 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3628 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3629  
3630  * @constructor
3631  * Create a new Menu
3632  * @param {Object} config The config objectQ
3633  */
3634
3635
3636 Roo.bootstrap.menu.Menu = function(config){
3637     
3638     if (config.type == 'treeview') {
3639         // normally menu's are drawn attached to the document to handle layering etc..
3640         // however treeview (used by the docs menu is drawn into the parent element)
3641         this.container_method = 'getChildContainer'; 
3642     }
3643     
3644     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3645     if (this.registerMenu && this.type != 'treeview')  {
3646         Roo.bootstrap.menu.Manager.register(this);
3647     }
3648     
3649     
3650     this.addEvents({
3651         /**
3652          * @event beforeshow
3653          * Fires before this menu is displayed (return false to block)
3654          * @param {Roo.menu.Menu} this
3655          */
3656         beforeshow : true,
3657         /**
3658          * @event beforehide
3659          * Fires before this menu is hidden (return false to block)
3660          * @param {Roo.menu.Menu} this
3661          */
3662         beforehide : true,
3663         /**
3664          * @event show
3665          * Fires after this menu is displayed
3666          * @param {Roo.menu.Menu} this
3667          */
3668         show : true,
3669         /**
3670          * @event hide
3671          * Fires after this menu is hidden
3672          * @param {Roo.menu.Menu} this
3673          */
3674         hide : true,
3675         /**
3676          * @event click
3677          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3678          * @param {Roo.menu.Menu} this
3679          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680          * @param {Roo.EventObject} e
3681          */
3682         click : true,
3683         /**
3684          * @event mouseover
3685          * Fires when the mouse is hovering over this menu
3686          * @param {Roo.menu.Menu} this
3687          * @param {Roo.EventObject} e
3688          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3689          */
3690         mouseover : true,
3691         /**
3692          * @event mouseout
3693          * Fires when the mouse exits this menu
3694          * @param {Roo.menu.Menu} this
3695          * @param {Roo.EventObject} e
3696          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3697          */
3698         mouseout : true,
3699         /**
3700          * @event itemclick
3701          * Fires when a menu item contained in this menu is clicked
3702          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3703          * @param {Roo.EventObject} e
3704          */
3705         itemclick: true
3706     });
3707     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3708 };
3709
3710 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3711     
3712    /// html : false,
3713    
3714     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3715     type: false,
3716     /**
3717      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3718      */
3719     registerMenu : true,
3720     
3721     menuItems :false, // stores the menu items..
3722     
3723     hidden:true,
3724         
3725     parentMenu : false,
3726     
3727     stopEvent : true,
3728     
3729     isLink : false,
3730     
3731     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3732     
3733     hideTrigger : false,
3734     
3735     align : 'tl-bl?',
3736     
3737     
3738     getChildContainer : function() {
3739         return this.el;  
3740     },
3741     
3742     getAutoCreate : function(){
3743          
3744         //if (['right'].indexOf(this.align)!==-1) {
3745         //    cfg.cn[1].cls += ' pull-right'
3746         //}
3747          
3748         var cfg = {
3749             tag : 'ul',
3750             cls : 'dropdown-menu shadow' ,
3751             style : 'z-index:1000'
3752             
3753         };
3754         
3755         if (this.type === 'submenu') {
3756             cfg.cls = 'submenu active';
3757         }
3758         if (this.type === 'treeview') {
3759             cfg.cls = 'treeview-menu';
3760         }
3761         
3762         return cfg;
3763     },
3764     initEvents : function() {
3765         
3766        // Roo.log("ADD event");
3767        // Roo.log(this.triggerEl.dom);
3768         if (this.triggerEl) {
3769             
3770             this.triggerEl.on('click', this.onTriggerClick, this);
3771             
3772             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3773             
3774             if (!this.hideTrigger) {
3775                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3776                     // dropdown toggle on the 'a' in BS4?
3777                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3778                 } else {
3779                     this.triggerEl.addClass('dropdown-toggle');
3780                 }
3781             }
3782         }
3783         
3784         if (Roo.isTouch) {
3785             this.el.on('touchstart'  , this.onTouch, this);
3786         }
3787         this.el.on('click' , this.onClick, this);
3788
3789         this.el.on("mouseover", this.onMouseOver, this);
3790         this.el.on("mouseout", this.onMouseOut, this);
3791         
3792     },
3793     
3794     findTargetItem : function(e)
3795     {
3796         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3797         if(!t){
3798             return false;
3799         }
3800         //Roo.log(t);         Roo.log(t.id);
3801         if(t && t.id){
3802             //Roo.log(this.menuitems);
3803             return this.menuitems.get(t.id);
3804             
3805             //return this.items.get(t.menuItemId);
3806         }
3807         
3808         return false;
3809     },
3810     
3811     onTouch : function(e) 
3812     {
3813         Roo.log("menu.onTouch");
3814         //e.stopEvent(); this make the user popdown broken
3815         this.onClick(e);
3816     },
3817     
3818     onClick : function(e)
3819     {
3820         Roo.log("menu.onClick");
3821         
3822         var t = this.findTargetItem(e);
3823         if(!t || t.isContainer){
3824             return;
3825         }
3826         Roo.log(e);
3827         /*
3828         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3829             if(t == this.activeItem && t.shouldDeactivate(e)){
3830                 this.activeItem.deactivate();
3831                 delete this.activeItem;
3832                 return;
3833             }
3834             if(t.canActivate){
3835                 this.setActiveItem(t, true);
3836             }
3837             return;
3838             
3839             
3840         }
3841         */
3842        
3843         Roo.log('pass click event');
3844         
3845         t.onClick(e);
3846         
3847         this.fireEvent("click", this, t, e);
3848         
3849         var _this = this;
3850         
3851         if(!t.href.length || t.href == '#'){
3852             (function() { _this.hide(); }).defer(100);
3853         }
3854         
3855     },
3856     
3857     onMouseOver : function(e){
3858         var t  = this.findTargetItem(e);
3859         //Roo.log(t);
3860         //if(t){
3861         //    if(t.canActivate && !t.disabled){
3862         //        this.setActiveItem(t, true);
3863         //    }
3864         //}
3865         
3866         this.fireEvent("mouseover", this, e, t);
3867     },
3868     isVisible : function(){
3869         return !this.hidden;
3870     },
3871     onMouseOut : function(e){
3872         var t  = this.findTargetItem(e);
3873         
3874         //if(t ){
3875         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3876         //        this.activeItem.deactivate();
3877         //        delete this.activeItem;
3878         //    }
3879         //}
3880         this.fireEvent("mouseout", this, e, t);
3881     },
3882     
3883     
3884     /**
3885      * Displays this menu relative to another element
3886      * @param {String/HTMLElement/Roo.Element} element The element to align to
3887      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3888      * the element (defaults to this.defaultAlign)
3889      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3890      */
3891     show : function(el, pos, parentMenu)
3892     {
3893         if (false === this.fireEvent("beforeshow", this)) {
3894             Roo.log("show canceled");
3895             return;
3896         }
3897         this.parentMenu = parentMenu;
3898         if(!this.el){
3899             this.render();
3900         }
3901         this.el.addClass('show'); // show otherwise we do not know how big we are..
3902          
3903         var xy = this.el.getAlignToXY(el, pos);
3904         
3905         // bl-tl << left align  below
3906         // tl-bl << left align 
3907         
3908         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3909             // if it goes to far to the right.. -> align left.
3910             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3911         }
3912         if(xy[0] < 0){
3913             // was left align - go right?
3914             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3915         }
3916         
3917         // goes down the bottom
3918         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3919            xy[1]  < 0 ){
3920             var a = this.align.replace('?', '').split('-');
3921             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3922             
3923         }
3924         
3925         this.showAt(  xy , parentMenu, false);
3926     },
3927      /**
3928      * Displays this menu at a specific xy position
3929      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3930      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3931      */
3932     showAt : function(xy, parentMenu, /* private: */_e){
3933         this.parentMenu = parentMenu;
3934         if(!this.el){
3935             this.render();
3936         }
3937         if(_e !== false){
3938             this.fireEvent("beforeshow", this);
3939             //xy = this.el.adjustForConstraints(xy);
3940         }
3941         
3942         //this.el.show();
3943         this.hideMenuItems();
3944         this.hidden = false;
3945         if (this.triggerEl) {
3946             this.triggerEl.addClass('open');
3947         }
3948         
3949         this.el.addClass('show');
3950         
3951         
3952         
3953         // reassign x when hitting right
3954         
3955         // reassign y when hitting bottom
3956         
3957         // but the list may align on trigger left or trigger top... should it be a properity?
3958         
3959         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3960             this.el.setXY(xy);
3961         }
3962         
3963         this.focus();
3964         this.fireEvent("show", this);
3965     },
3966     
3967     focus : function(){
3968         return;
3969         if(!this.hidden){
3970             this.doFocus.defer(50, this);
3971         }
3972     },
3973
3974     doFocus : function(){
3975         if(!this.hidden){
3976             this.focusEl.focus();
3977         }
3978     },
3979
3980     /**
3981      * Hides this menu and optionally all parent menus
3982      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3983      */
3984     hide : function(deep)
3985     {
3986         if (false === this.fireEvent("beforehide", this)) {
3987             Roo.log("hide canceled");
3988             return;
3989         }
3990         this.hideMenuItems();
3991         if(this.el && this.isVisible()){
3992            
3993             if(this.activeItem){
3994                 this.activeItem.deactivate();
3995                 this.activeItem = null;
3996             }
3997             if (this.triggerEl) {
3998                 this.triggerEl.removeClass('open');
3999             }
4000             
4001             this.el.removeClass('show');
4002             this.hidden = true;
4003             this.fireEvent("hide", this);
4004         }
4005         if(deep === true && this.parentMenu){
4006             this.parentMenu.hide(true);
4007         }
4008     },
4009     
4010     onTriggerClick : function(e)
4011     {
4012         Roo.log('trigger click');
4013         
4014         var target = e.getTarget();
4015         
4016         Roo.log(target.nodeName.toLowerCase());
4017         
4018         if(target.nodeName.toLowerCase() === 'i'){
4019             e.preventDefault();
4020         }
4021         
4022     },
4023     
4024     onTriggerPress  : function(e)
4025     {
4026         Roo.log('trigger press');
4027         //Roo.log(e.getTarget());
4028        // Roo.log(this.triggerEl.dom);
4029        
4030         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4031         var pel = Roo.get(e.getTarget());
4032         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4033             Roo.log('is treeview or dropdown?');
4034             return;
4035         }
4036         
4037         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4038             return;
4039         }
4040         
4041         if (this.isVisible()) {
4042             Roo.log('hide');
4043             this.hide();
4044         } else {
4045             Roo.log('show');
4046             
4047             this.show(this.triggerEl, this.align, false);
4048         }
4049         
4050         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4051             e.stopEvent();
4052         }
4053         
4054     },
4055        
4056     
4057     hideMenuItems : function()
4058     {
4059         Roo.log("hide Menu Items");
4060         if (!this.el) { 
4061             return;
4062         }
4063         
4064         this.el.select('.open',true).each(function(aa) {
4065             
4066             aa.removeClass('open');
4067          
4068         });
4069     },
4070     addxtypeChild : function (tree, cntr) {
4071         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4072           
4073         this.menuitems.add(comp);
4074         return comp;
4075
4076     },
4077     getEl : function()
4078     {
4079         Roo.log(this.el);
4080         return this.el;
4081     },
4082     
4083     clear : function()
4084     {
4085         this.getEl().dom.innerHTML = '';
4086         this.menuitems.clear();
4087     }
4088 });
4089
4090  
4091  /**
4092  * @class Roo.bootstrap.menu.Item
4093  * @extends Roo.bootstrap.Component
4094  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4095  * @parent Roo.bootstrap.menu.Menu
4096  * @licence LGPL
4097  * Bootstrap MenuItem class
4098  * 
4099  * @cfg {String} html the menu label
4100  * @cfg {String} href the link
4101  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4102  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4103  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4104  * @cfg {String} fa favicon to show on left of menu item.
4105  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4106  * 
4107  * 
4108  * @constructor
4109  * Create a new MenuItem
4110  * @param {Object} config The config object
4111  */
4112
4113
4114 Roo.bootstrap.menu.Item = function(config){
4115     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4116     this.addEvents({
4117         // raw events
4118         /**
4119          * @event click
4120          * The raw click event for the entire grid.
4121          * @param {Roo.bootstrap.menu.Item} this
4122          * @param {Roo.EventObject} e
4123          */
4124         "click" : true
4125     });
4126 };
4127
4128 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4129     
4130     href : false,
4131     html : false,
4132     preventDefault: false,
4133     isContainer : false,
4134     active : false,
4135     fa: false,
4136     
4137     getAutoCreate : function(){
4138         
4139         if(this.isContainer){
4140             return {
4141                 tag: 'li',
4142                 cls: 'dropdown-menu-item '
4143             };
4144         }
4145         var ctag = {
4146             tag: 'span',
4147             html: 'Link'
4148         };
4149         
4150         var anc = {
4151             tag : 'a',
4152             cls : 'dropdown-item',
4153             href : '#',
4154             cn : [  ]
4155         };
4156         
4157         if (this.fa !== false) {
4158             anc.cn.push({
4159                 tag : 'i',
4160                 cls : 'fa fa-' + this.fa
4161             });
4162         }
4163         
4164         anc.cn.push(ctag);
4165         
4166         
4167         var cfg= {
4168             tag: 'li',
4169             cls: 'dropdown-menu-item',
4170             cn: [ anc ]
4171         };
4172         if (this.parent().type == 'treeview') {
4173             cfg.cls = 'treeview-menu';
4174         }
4175         if (this.active) {
4176             cfg.cls += ' active';
4177         }
4178         
4179         
4180         
4181         anc.href = this.href || cfg.cn[0].href ;
4182         ctag.html = this.html || cfg.cn[0].html ;
4183         return cfg;
4184     },
4185     
4186     initEvents: function()
4187     {
4188         if (this.parent().type == 'treeview') {
4189             this.el.select('a').on('click', this.onClick, this);
4190         }
4191         
4192         if (this.menu) {
4193             this.menu.parentType = this.xtype;
4194             this.menu.triggerEl = this.el;
4195             this.menu = this.addxtype(Roo.apply({}, this.menu));
4196         }
4197         
4198     },
4199     onClick : function(e)
4200     {
4201         Roo.log('item on click ');
4202         
4203         if(this.preventDefault){
4204             e.preventDefault();
4205         }
4206         //this.parent().hideMenuItems();
4207         
4208         this.fireEvent('click', this, e);
4209     },
4210     getEl : function()
4211     {
4212         return this.el;
4213     } 
4214 });
4215
4216  
4217
4218  
4219
4220   
4221 /**
4222  * @class Roo.bootstrap.menu.Separator
4223  * @extends Roo.bootstrap.Component
4224  * @licence LGPL
4225  * @parent Roo.bootstrap.menu.Menu
4226  * Bootstrap Separator class
4227  * 
4228  * @constructor
4229  * Create a new Separator
4230  * @param {Object} config The config object
4231  */
4232
4233
4234 Roo.bootstrap.menu.Separator = function(config){
4235     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4236 };
4237
4238 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4239     
4240     getAutoCreate : function(){
4241         var cfg = {
4242             tag : 'li',
4243             cls: 'dropdown-divider divider'
4244         };
4245         
4246         return cfg;
4247     }
4248    
4249 });
4250
4251  
4252
4253  
4254 /*
4255 * Licence: LGPL
4256 */
4257
4258 /**
4259  * @class Roo.bootstrap.Modal
4260  * @extends Roo.bootstrap.Component
4261  * @parent none builder
4262  * @children Roo.bootstrap.Component
4263  * Bootstrap Modal class
4264  * @cfg {String} title Title of dialog
4265  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4266  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4267  * @cfg {Boolean} specificTitle default false
4268  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4269  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4270  * @cfg {Boolean} animate default true
4271  * @cfg {Boolean} allow_close default true
4272  * @cfg {Boolean} fitwindow default false
4273  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4274  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4275  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4276  * @cfg {String} size (sm|lg|xl) default empty
4277  * @cfg {Number} max_width set the max width of modal
4278  * @cfg {Boolean} editableTitle can the title be edited
4279
4280  *
4281  *
4282  * @constructor
4283  * Create a new Modal Dialog
4284  * @param {Object} config The config object
4285  */
4286
4287 Roo.bootstrap.Modal = function(config){
4288     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4289     this.addEvents({
4290         // raw events
4291         /**
4292          * @event btnclick
4293          * The raw btnclick event for the button
4294          * @param {Roo.EventObject} e
4295          */
4296         "btnclick" : true,
4297         /**
4298          * @event resize
4299          * Fire when dialog resize
4300          * @param {Roo.bootstrap.Modal} this
4301          * @param {Roo.EventObject} e
4302          */
4303         "resize" : true,
4304         /**
4305          * @event titlechanged
4306          * Fire when the editable title has been changed
4307          * @param {Roo.bootstrap.Modal} this
4308          * @param {Roo.EventObject} value
4309          */
4310         "titlechanged" : true 
4311         
4312     });
4313     this.buttons = this.buttons || [];
4314
4315     if (this.tmpl) {
4316         this.tmpl = Roo.factory(this.tmpl);
4317     }
4318
4319 };
4320
4321 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4322
4323     title : 'test dialog',
4324
4325     buttons : false,
4326
4327     // set on load...
4328
4329     html: false,
4330
4331     tmp: false,
4332
4333     specificTitle: false,
4334
4335     buttonPosition: 'right',
4336
4337     allow_close : true,
4338
4339     animate : true,
4340
4341     fitwindow: false,
4342     
4343      // private
4344     dialogEl: false,
4345     bodyEl:  false,
4346     footerEl:  false,
4347     titleEl:  false,
4348     closeEl:  false,
4349
4350     size: '',
4351     
4352     max_width: 0,
4353     
4354     max_height: 0,
4355     
4356     fit_content: false,
4357     editableTitle  : false,
4358
4359     onRender : function(ct, position)
4360     {
4361         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4362
4363         if(!this.el){
4364             var cfg = Roo.apply({},  this.getAutoCreate());
4365             cfg.id = Roo.id();
4366             //if(!cfg.name){
4367             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4368             //}
4369             //if (!cfg.name.length) {
4370             //    delete cfg.name;
4371            // }
4372             if (this.cls) {
4373                 cfg.cls += ' ' + this.cls;
4374             }
4375             if (this.style) {
4376                 cfg.style = this.style;
4377             }
4378             this.el = Roo.get(document.body).createChild(cfg, position);
4379         }
4380         //var type = this.el.dom.type;
4381
4382
4383         if(this.tabIndex !== undefined){
4384             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4385         }
4386
4387         this.dialogEl = this.el.select('.modal-dialog',true).first();
4388         this.bodyEl = this.el.select('.modal-body',true).first();
4389         this.closeEl = this.el.select('.modal-header .close', true).first();
4390         this.headerEl = this.el.select('.modal-header',true).first();
4391         this.titleEl = this.el.select('.modal-title',true).first();
4392         this.footerEl = this.el.select('.modal-footer',true).first();
4393
4394         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4395         
4396         //this.el.addClass("x-dlg-modal");
4397
4398         if (this.buttons.length) {
4399             Roo.each(this.buttons, function(bb) {
4400                 var b = Roo.apply({}, bb);
4401                 b.xns = b.xns || Roo.bootstrap;
4402                 b.xtype = b.xtype || 'Button';
4403                 if (typeof(b.listeners) == 'undefined') {
4404                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4405                 }
4406
4407                 var btn = Roo.factory(b);
4408
4409                 btn.render(this.getButtonContainer());
4410
4411             },this);
4412         }
4413         // render the children.
4414         var nitems = [];
4415
4416         if(typeof(this.items) != 'undefined'){
4417             var items = this.items;
4418             delete this.items;
4419
4420             for(var i =0;i < items.length;i++) {
4421                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4422             }
4423         }
4424
4425         this.items = nitems;
4426
4427         // where are these used - they used to be body/close/footer
4428
4429
4430         this.initEvents();
4431         //this.el.addClass([this.fieldClass, this.cls]);
4432
4433     },
4434
4435     getAutoCreate : function()
4436     {
4437         // we will default to modal-body-overflow - might need to remove or make optional later.
4438         var bdy = {
4439                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4440                 html : this.html || ''
4441         };
4442
4443         var title = {
4444             tag: 'h5',
4445             cls : 'modal-title',
4446             html : this.title
4447         };
4448
4449         if(this.specificTitle){ // WTF is this?
4450             title = this.title;
4451         }
4452
4453         var header = [];
4454         if (this.allow_close && Roo.bootstrap.version == 3) {
4455             header.push({
4456                 tag: 'button',
4457                 cls : 'close',
4458                 html : '&times'
4459             });
4460         }
4461
4462         header.push(title);
4463
4464         if (this.editableTitle) {
4465             header.push({
4466                 cls: 'form-control roo-editable-title d-none',
4467                 tag: 'input',
4468                 type: 'text'
4469             });
4470         }
4471         
4472         if (this.allow_close && Roo.bootstrap.version == 4) {
4473             header.push({
4474                 tag: 'button',
4475                 cls : 'close',
4476                 html : '&times'
4477             });
4478         }
4479         
4480         var size = '';
4481
4482         if(this.size.length){
4483             size = 'modal-' + this.size;
4484         }
4485         
4486         var footer = Roo.bootstrap.version == 3 ?
4487             {
4488                 cls : 'modal-footer',
4489                 cn : [
4490                     {
4491                         tag: 'div',
4492                         cls: 'btn-' + this.buttonPosition
4493                     }
4494                 ]
4495
4496             } :
4497             {  // BS4 uses mr-auto on left buttons....
4498                 cls : 'modal-footer'
4499             };
4500
4501             
4502
4503         
4504         
4505         var modal = {
4506             cls: "modal",
4507              cn : [
4508                 {
4509                     cls: "modal-dialog " + size,
4510                     cn : [
4511                         {
4512                             cls : "modal-content",
4513                             cn : [
4514                                 {
4515                                     cls : 'modal-header',
4516                                     cn : header
4517                                 },
4518                                 bdy,
4519                                 footer
4520                             ]
4521
4522                         }
4523                     ]
4524
4525                 }
4526             ]
4527         };
4528
4529         if(this.animate){
4530             modal.cls += ' fade';
4531         }
4532
4533         return modal;
4534
4535     },
4536     getChildContainer : function() {
4537
4538          return this.bodyEl;
4539
4540     },
4541     getButtonContainer : function() {
4542         
4543          return Roo.bootstrap.version == 4 ?
4544             this.el.select('.modal-footer',true).first()
4545             : this.el.select('.modal-footer div',true).first();
4546
4547     },
4548     initEvents : function()
4549     {
4550         if (this.allow_close) {
4551             this.closeEl.on('click', this.hide, this);
4552         }
4553         Roo.EventManager.onWindowResize(this.resize, this, true);
4554         if (this.editableTitle) {
4555             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4556             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557             this.headerEditEl.on('keyup', function(e) {
4558                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559                         this.toggleHeaderInput(false)
4560                     }
4561                 }, this);
4562             this.headerEditEl.on('blur', function(e) {
4563                 this.toggleHeaderInput(false)
4564             },this);
4565         }
4566
4567     },
4568   
4569
4570     resize : function()
4571     {
4572         this.maskEl.setSize(
4573             Roo.lib.Dom.getViewWidth(true),
4574             Roo.lib.Dom.getViewHeight(true)
4575         );
4576         
4577         if (this.fitwindow) {
4578             
4579            this.dialogEl.setStyle( { 'max-width' : '100%' });
4580             this.setSize(
4581                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4583             );
4584             return;
4585         }
4586         
4587         if(this.max_width !== 0) {
4588             
4589             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4590             
4591             if(this.height) {
4592                 this.setSize(w, this.height);
4593                 return;
4594             }
4595             
4596             if(this.max_height) {
4597                 this.setSize(w,Math.min(
4598                     this.max_height,
4599                     Roo.lib.Dom.getViewportHeight(true) - 60
4600                 ));
4601                 
4602                 return;
4603             }
4604             
4605             if(!this.fit_content) {
4606                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4607                 return;
4608             }
4609             
4610             this.setSize(w, Math.min(
4611                 60 +
4612                 this.headerEl.getHeight() + 
4613                 this.footerEl.getHeight() + 
4614                 this.getChildHeight(this.bodyEl.dom.childNodes),
4615                 Roo.lib.Dom.getViewportHeight(true) - 60)
4616             );
4617         }
4618         
4619     },
4620
4621     setSize : function(w,h)
4622     {
4623         if (!w && !h) {
4624             return;
4625         }
4626         
4627         this.resizeTo(w,h);
4628     },
4629
4630     show : function() {
4631
4632         if (!this.rendered) {
4633             this.render();
4634         }
4635         this.toggleHeaderInput(false);
4636         //this.el.setStyle('display', 'block');
4637         this.el.removeClass('hideing');
4638         this.el.dom.style.display='block';
4639         
4640         Roo.get(document.body).addClass('modal-open');
4641  
4642         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4643             
4644             (function(){
4645                 this.el.addClass('show');
4646                 this.el.addClass('in');
4647             }).defer(50, this);
4648         }else{
4649             this.el.addClass('show');
4650             this.el.addClass('in');
4651         }
4652
4653         // not sure how we can show data in here..
4654         //if (this.tmpl) {
4655         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4656         //}
4657
4658         Roo.get(document.body).addClass("x-body-masked");
4659         
4660         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4661         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4662         this.maskEl.dom.style.display = 'block';
4663         this.maskEl.addClass('show');
4664         
4665         
4666         this.resize();
4667         
4668         this.fireEvent('show', this);
4669
4670         // set zindex here - otherwise it appears to be ignored...
4671         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4672
4673         (function () {
4674             this.items.forEach( function(e) {
4675                 e.layout ? e.layout() : false;
4676
4677             });
4678         }).defer(100,this);
4679
4680     },
4681     hide : function()
4682     {
4683         if(this.fireEvent("beforehide", this) !== false){
4684             
4685             this.maskEl.removeClass('show');
4686             
4687             this.maskEl.dom.style.display = '';
4688             Roo.get(document.body).removeClass("x-body-masked");
4689             this.el.removeClass('in');
4690             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4691
4692             if(this.animate){ // why
4693                 this.el.addClass('hideing');
4694                 this.el.removeClass('show');
4695                 (function(){
4696                     if (!this.el.hasClass('hideing')) {
4697                         return; // it's been shown again...
4698                     }
4699                     
4700                     this.el.dom.style.display='';
4701
4702                     Roo.get(document.body).removeClass('modal-open');
4703                     this.el.removeClass('hideing');
4704                 }).defer(150,this);
4705                 
4706             }else{
4707                 this.el.removeClass('show');
4708                 this.el.dom.style.display='';
4709                 Roo.get(document.body).removeClass('modal-open');
4710
4711             }
4712             this.fireEvent('hide', this);
4713         }
4714     },
4715     isVisible : function()
4716     {
4717         
4718         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4719         
4720     },
4721
4722     addButton : function(str, cb)
4723     {
4724
4725
4726         var b = Roo.apply({}, { html : str } );
4727         b.xns = b.xns || Roo.bootstrap;
4728         b.xtype = b.xtype || 'Button';
4729         if (typeof(b.listeners) == 'undefined') {
4730             b.listeners = { click : cb.createDelegate(this)  };
4731         }
4732
4733         var btn = Roo.factory(b);
4734
4735         btn.render(this.getButtonContainer());
4736
4737         return btn;
4738
4739     },
4740
4741     setDefaultButton : function(btn)
4742     {
4743         //this.el.select('.modal-footer').()
4744     },
4745
4746     resizeTo: function(w,h)
4747     {
4748         this.dialogEl.setWidth(w);
4749         
4750         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4751
4752         this.bodyEl.setHeight(h - diff);
4753         
4754         this.fireEvent('resize', this);
4755     },
4756     
4757     setContentSize  : function(w, h)
4758     {
4759
4760     },
4761     onButtonClick: function(btn,e)
4762     {
4763         //Roo.log([a,b,c]);
4764         this.fireEvent('btnclick', btn.name, e);
4765     },
4766      /**
4767      * Set the title of the Dialog
4768      * @param {String} str new Title
4769      */
4770     setTitle: function(str) {
4771         this.titleEl.dom.innerHTML = str;
4772         this.title = str;
4773     },
4774     /**
4775      * Set the body of the Dialog
4776      * @param {String} str new Title
4777      */
4778     setBody: function(str) {
4779         this.bodyEl.dom.innerHTML = str;
4780     },
4781     /**
4782      * Set the body of the Dialog using the template
4783      * @param {Obj} data - apply this data to the template and replace the body contents.
4784      */
4785     applyBody: function(obj)
4786     {
4787         if (!this.tmpl) {
4788             Roo.log("Error - using apply Body without a template");
4789             //code
4790         }
4791         this.tmpl.overwrite(this.bodyEl, obj);
4792     },
4793     
4794     getChildHeight : function(child_nodes)
4795     {
4796         if(
4797             !child_nodes ||
4798             child_nodes.length == 0
4799         ) {
4800             return 0;
4801         }
4802         
4803         var child_height = 0;
4804         
4805         for(var i = 0; i < child_nodes.length; i++) {
4806             
4807             /*
4808             * for modal with tabs...
4809             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4810                 
4811                 var layout_childs = child_nodes[i].childNodes;
4812                 
4813                 for(var j = 0; j < layout_childs.length; j++) {
4814                     
4815                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4816                         
4817                         var layout_body_childs = layout_childs[j].childNodes;
4818                         
4819                         for(var k = 0; k < layout_body_childs.length; k++) {
4820                             
4821                             if(layout_body_childs[k].classList.contains('navbar')) {
4822                                 child_height += layout_body_childs[k].offsetHeight;
4823                                 continue;
4824                             }
4825                             
4826                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4827                                 
4828                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4829                                 
4830                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4831                                     
4832                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4833                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4834                                         continue;
4835                                     }
4836                                     
4837                                 }
4838                                 
4839                             }
4840                             
4841                         }
4842                     }
4843                 }
4844                 continue;
4845             }
4846             */
4847             
4848             child_height += child_nodes[i].offsetHeight;
4849             // Roo.log(child_nodes[i].offsetHeight);
4850         }
4851         
4852         return child_height;
4853     },
4854     toggleHeaderInput : function(is_edit)
4855     {
4856         if (!this.editableTitle) {
4857             return; // not editable.
4858         }
4859         if (is_edit && this.is_header_editing) {
4860             return; // already editing..
4861         }
4862         if (is_edit) {
4863     
4864             this.headerEditEl.dom.value = this.title;
4865             this.headerEditEl.removeClass('d-none');
4866             this.headerEditEl.dom.focus();
4867             this.titleEl.addClass('d-none');
4868             
4869             this.is_header_editing = true;
4870             return
4871         }
4872         // flip back to not editing.
4873         this.title = this.headerEditEl.dom.value;
4874         this.headerEditEl.addClass('d-none');
4875         this.titleEl.removeClass('d-none');
4876         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4877         this.is_header_editing = false;
4878         this.fireEvent('titlechanged', this, this.title);
4879     
4880             
4881         
4882     }
4883
4884 });
4885
4886
4887 Roo.apply(Roo.bootstrap.Modal,  {
4888     /**
4889          * Button config that displays a single OK button
4890          * @type Object
4891          */
4892         OK :  [{
4893             name : 'ok',
4894             weight : 'primary',
4895             html : 'OK'
4896         }],
4897         /**
4898          * Button config that displays Yes and No buttons
4899          * @type Object
4900          */
4901         YESNO : [
4902             {
4903                 name  : 'no',
4904                 html : 'No'
4905             },
4906             {
4907                 name  :'yes',
4908                 weight : 'primary',
4909                 html : 'Yes'
4910             }
4911         ],
4912
4913         /**
4914          * Button config that displays OK and Cancel buttons
4915          * @type Object
4916          */
4917         OKCANCEL : [
4918             {
4919                name : 'cancel',
4920                 html : 'Cancel'
4921             },
4922             {
4923                 name : 'ok',
4924                 weight : 'primary',
4925                 html : 'OK'
4926             }
4927         ],
4928         /**
4929          * Button config that displays Yes, No and Cancel buttons
4930          * @type Object
4931          */
4932         YESNOCANCEL : [
4933             {
4934                 name : 'yes',
4935                 weight : 'primary',
4936                 html : 'Yes'
4937             },
4938             {
4939                 name : 'no',
4940                 html : 'No'
4941             },
4942             {
4943                 name : 'cancel',
4944                 html : 'Cancel'
4945             }
4946         ],
4947         
4948         zIndex : 10001
4949 });
4950
4951 /*
4952  * - LGPL
4953  *
4954  * messagebox - can be used as a replace
4955  * 
4956  */
4957 /**
4958  * @class Roo.MessageBox
4959  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4960  * Example usage:
4961  *<pre><code>
4962 // Basic alert:
4963 Roo.Msg.alert('Status', 'Changes saved successfully.');
4964
4965 // Prompt for user data:
4966 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4967     if (btn == 'ok'){
4968         // process text value...
4969     }
4970 });
4971
4972 // Show a dialog using config options:
4973 Roo.Msg.show({
4974    title:'Save Changes?',
4975    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4976    buttons: Roo.Msg.YESNOCANCEL,
4977    fn: processResult,
4978    animEl: 'elId'
4979 });
4980 </code></pre>
4981  * @static
4982  */
4983 Roo.bootstrap.MessageBox = function(){
4984     var dlg, opt, mask, waitTimer;
4985     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4986     var buttons, activeTextEl, bwidth;
4987
4988     
4989     // private
4990     var handleButton = function(button){
4991         dlg.hide();
4992         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4993     };
4994
4995     // private
4996     var handleHide = function(){
4997         if(opt && opt.cls){
4998             dlg.el.removeClass(opt.cls);
4999         }
5000         //if(waitTimer){
5001         //    Roo.TaskMgr.stop(waitTimer);
5002         //    waitTimer = null;
5003         //}
5004     };
5005
5006     // private
5007     var updateButtons = function(b){
5008         var width = 0;
5009         if(!b){
5010             buttons["ok"].hide();
5011             buttons["cancel"].hide();
5012             buttons["yes"].hide();
5013             buttons["no"].hide();
5014             dlg.footerEl.hide();
5015             
5016             return width;
5017         }
5018         dlg.footerEl.show();
5019         for(var k in buttons){
5020             if(typeof buttons[k] != "function"){
5021                 if(b[k]){
5022                     buttons[k].show();
5023                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5024                     width += buttons[k].el.getWidth()+15;
5025                 }else{
5026                     buttons[k].hide();
5027                 }
5028             }
5029         }
5030         return width;
5031     };
5032
5033     // private
5034     var handleEsc = function(d, k, e){
5035         if(opt && opt.closable !== false){
5036             dlg.hide();
5037         }
5038         if(e){
5039             e.stopEvent();
5040         }
5041     };
5042
5043     return {
5044         /**
5045          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5046          * @return {Roo.BasicDialog} The BasicDialog element
5047          */
5048         getDialog : function(){
5049            if(!dlg){
5050                 dlg = new Roo.bootstrap.Modal( {
5051                     //draggable: true,
5052                     //resizable:false,
5053                     //constraintoviewport:false,
5054                     //fixedcenter:true,
5055                     //collapsible : false,
5056                     //shim:true,
5057                     //modal: true,
5058                 //    width: 'auto',
5059                   //  height:100,
5060                     //buttonAlign:"center",
5061                     closeClick : function(){
5062                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5063                             handleButton("no");
5064                         }else{
5065                             handleButton("cancel");
5066                         }
5067                     }
5068                 });
5069                 dlg.render();
5070                 dlg.on("hide", handleHide);
5071                 mask = dlg.mask;
5072                 //dlg.addKeyListener(27, handleEsc);
5073                 buttons = {};
5074                 this.buttons = buttons;
5075                 var bt = this.buttonText;
5076                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5077                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5078                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5079                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5080                 //Roo.log(buttons);
5081                 bodyEl = dlg.bodyEl.createChild({
5082
5083                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5084                         '<textarea class="roo-mb-textarea"></textarea>' +
5085                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5086                 });
5087                 msgEl = bodyEl.dom.firstChild;
5088                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5089                 textboxEl.enableDisplayMode();
5090                 textboxEl.addKeyListener([10,13], function(){
5091                     if(dlg.isVisible() && opt && opt.buttons){
5092                         if(opt.buttons.ok){
5093                             handleButton("ok");
5094                         }else if(opt.buttons.yes){
5095                             handleButton("yes");
5096                         }
5097                     }
5098                 });
5099                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5100                 textareaEl.enableDisplayMode();
5101                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5102                 progressEl.enableDisplayMode();
5103                 
5104                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5105                 var pf = progressEl.dom.firstChild;
5106                 if (pf) {
5107                     pp = Roo.get(pf.firstChild);
5108                     pp.setHeight(pf.offsetHeight);
5109                 }
5110                 
5111             }
5112             return dlg;
5113         },
5114
5115         /**
5116          * Updates the message box body text
5117          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5118          * the XHTML-compliant non-breaking space character '&amp;#160;')
5119          * @return {Roo.MessageBox} This message box
5120          */
5121         updateText : function(text)
5122         {
5123             if(!dlg.isVisible() && !opt.width){
5124                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5125                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5126             }
5127             msgEl.innerHTML = text || '&#160;';
5128       
5129             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5130             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5131             var w = Math.max(
5132                     Math.min(opt.width || cw , this.maxWidth), 
5133                     Math.max(opt.minWidth || this.minWidth, bwidth)
5134             );
5135             if(opt.prompt){
5136                 activeTextEl.setWidth(w);
5137             }
5138             if(dlg.isVisible()){
5139                 dlg.fixedcenter = false;
5140             }
5141             // to big, make it scroll. = But as usual stupid IE does not support
5142             // !important..
5143             
5144             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5145                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5146                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5147             } else {
5148                 bodyEl.dom.style.height = '';
5149                 bodyEl.dom.style.overflowY = '';
5150             }
5151             if (cw > w) {
5152                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5153             } else {
5154                 bodyEl.dom.style.overflowX = '';
5155             }
5156             
5157             dlg.setContentSize(w, bodyEl.getHeight());
5158             if(dlg.isVisible()){
5159                 dlg.fixedcenter = true;
5160             }
5161             return this;
5162         },
5163
5164         /**
5165          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5166          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5167          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5168          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5169          * @return {Roo.MessageBox} This message box
5170          */
5171         updateProgress : function(value, text){
5172             if(text){
5173                 this.updateText(text);
5174             }
5175             
5176             if (pp) { // weird bug on my firefox - for some reason this is not defined
5177                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5178                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5179             }
5180             return this;
5181         },        
5182
5183         /**
5184          * Returns true if the message box is currently displayed
5185          * @return {Boolean} True if the message box is visible, else false
5186          */
5187         isVisible : function(){
5188             return dlg && dlg.isVisible();  
5189         },
5190
5191         /**
5192          * Hides the message box if it is displayed
5193          */
5194         hide : function(){
5195             if(this.isVisible()){
5196                 dlg.hide();
5197             }  
5198         },
5199
5200         /**
5201          * Displays a new message box, or reinitializes an existing message box, based on the config options
5202          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5203          * The following config object properties are supported:
5204          * <pre>
5205 Property    Type             Description
5206 ----------  ---------------  ------------------------------------------------------------------------------------
5207 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5208                                    closes (defaults to undefined)
5209 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5210                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5211 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5212                                    progress and wait dialogs will ignore this property and always hide the
5213                                    close button as they can only be closed programmatically.
5214 cls               String           A custom CSS class to apply to the message box element
5215 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5216                                    displayed (defaults to 75)
5217 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5218                                    function will be btn (the name of the button that was clicked, if applicable,
5219                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5220                                    Progress and wait dialogs will ignore this option since they do not respond to
5221                                    user actions and can only be closed programmatically, so any required function
5222                                    should be called by the same code after it closes the dialog.
5223 icon              String           A CSS class that provides a background image to be used as an icon for
5224                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5225 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5226 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5227 modal             Boolean          False to allow user interaction with the page while the message box is
5228                                    displayed (defaults to true)
5229 msg               String           A string that will replace the existing message box body text (defaults
5230                                    to the XHTML-compliant non-breaking space character '&#160;')
5231 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5232 progress          Boolean          True to display a progress bar (defaults to false)
5233 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5234 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5235 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5236 title             String           The title text
5237 value             String           The string value to set into the active textbox element if displayed
5238 wait              Boolean          True to display a progress bar (defaults to false)
5239 width             Number           The width of the dialog in pixels
5240 </pre>
5241          *
5242          * Example usage:
5243          * <pre><code>
5244 Roo.Msg.show({
5245    title: 'Address',
5246    msg: 'Please enter your address:',
5247    width: 300,
5248    buttons: Roo.MessageBox.OKCANCEL,
5249    multiline: true,
5250    fn: saveAddress,
5251    animEl: 'addAddressBtn'
5252 });
5253 </code></pre>
5254          * @param {Object} config Configuration options
5255          * @return {Roo.MessageBox} This message box
5256          */
5257         show : function(options)
5258         {
5259             
5260             // this causes nightmares if you show one dialog after another
5261             // especially on callbacks..
5262              
5263             if(this.isVisible()){
5264                 
5265                 this.hide();
5266                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5267                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5268                 Roo.log("New Dialog Message:" +  options.msg )
5269                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5270                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5271                 
5272             }
5273             var d = this.getDialog();
5274             opt = options;
5275             d.setTitle(opt.title || "&#160;");
5276             d.closeEl.setDisplayed(opt.closable !== false);
5277             activeTextEl = textboxEl;
5278             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5279             if(opt.prompt){
5280                 if(opt.multiline){
5281                     textboxEl.hide();
5282                     textareaEl.show();
5283                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5284                         opt.multiline : this.defaultTextHeight);
5285                     activeTextEl = textareaEl;
5286                 }else{
5287                     textboxEl.show();
5288                     textareaEl.hide();
5289                 }
5290             }else{
5291                 textboxEl.hide();
5292                 textareaEl.hide();
5293             }
5294             progressEl.setDisplayed(opt.progress === true);
5295             if (opt.progress) {
5296                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5297             }
5298             this.updateProgress(0);
5299             activeTextEl.dom.value = opt.value || "";
5300             if(opt.prompt){
5301                 dlg.setDefaultButton(activeTextEl);
5302             }else{
5303                 var bs = opt.buttons;
5304                 var db = null;
5305                 if(bs && bs.ok){
5306                     db = buttons["ok"];
5307                 }else if(bs && bs.yes){
5308                     db = buttons["yes"];
5309                 }
5310                 dlg.setDefaultButton(db);
5311             }
5312             bwidth = updateButtons(opt.buttons);
5313             this.updateText(opt.msg);
5314             if(opt.cls){
5315                 d.el.addClass(opt.cls);
5316             }
5317             d.proxyDrag = opt.proxyDrag === true;
5318             d.modal = opt.modal !== false;
5319             d.mask = opt.modal !== false ? mask : false;
5320             if(!d.isVisible()){
5321                 // force it to the end of the z-index stack so it gets a cursor in FF
5322                 document.body.appendChild(dlg.el.dom);
5323                 d.animateTarget = null;
5324                 d.show(options.animEl);
5325             }
5326             return this;
5327         },
5328
5329         /**
5330          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5331          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5332          * and closing the message box when the process is complete.
5333          * @param {String} title The title bar text
5334          * @param {String} msg The message box body text
5335          * @return {Roo.MessageBox} This message box
5336          */
5337         progress : function(title, msg){
5338             this.show({
5339                 title : title,
5340                 msg : msg,
5341                 buttons: false,
5342                 progress:true,
5343                 closable:false,
5344                 minWidth: this.minProgressWidth,
5345                 modal : true
5346             });
5347             return this;
5348         },
5349
5350         /**
5351          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5352          * If a callback function is passed it will be called after the user clicks the button, and the
5353          * id of the button that was clicked will be passed as the only parameter to the callback
5354          * (could also be the top-right close button).
5355          * @param {String} title The title bar text
5356          * @param {String} msg The message box body text
5357          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5358          * @param {Object} scope (optional) The scope of the callback function
5359          * @return {Roo.MessageBox} This message box
5360          */
5361         alert : function(title, msg, fn, scope)
5362         {
5363             this.show({
5364                 title : title,
5365                 msg : msg,
5366                 buttons: this.OK,
5367                 fn: fn,
5368                 closable : false,
5369                 scope : scope,
5370                 modal : true
5371             });
5372             return this;
5373         },
5374
5375         /**
5376          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5377          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5378          * You are responsible for closing the message box when the process is complete.
5379          * @param {String} msg The message box body text
5380          * @param {String} title (optional) The title bar text
5381          * @return {Roo.MessageBox} This message box
5382          */
5383         wait : function(msg, title){
5384             this.show({
5385                 title : title,
5386                 msg : msg,
5387                 buttons: false,
5388                 closable:false,
5389                 progress:true,
5390                 modal:true,
5391                 width:300,
5392                 wait:true
5393             });
5394             waitTimer = Roo.TaskMgr.start({
5395                 run: function(i){
5396                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5397                 },
5398                 interval: 1000
5399             });
5400             return this;
5401         },
5402
5403         /**
5404          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5405          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5406          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5407          * @param {String} title The title bar text
5408          * @param {String} msg The message box body text
5409          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5410          * @param {Object} scope (optional) The scope of the callback function
5411          * @return {Roo.MessageBox} This message box
5412          */
5413         confirm : function(title, msg, fn, scope){
5414             this.show({
5415                 title : title,
5416                 msg : msg,
5417                 buttons: this.YESNO,
5418                 fn: fn,
5419                 scope : scope,
5420                 modal : true
5421             });
5422             return this;
5423         },
5424
5425         /**
5426          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5427          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5428          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5429          * (could also be the top-right close button) and the text that was entered will be passed as the two
5430          * parameters to the callback.
5431          * @param {String} title The title bar text
5432          * @param {String} msg The message box body text
5433          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5434          * @param {Object} scope (optional) The scope of the callback function
5435          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5436          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5437          * @return {Roo.MessageBox} This message box
5438          */
5439         prompt : function(title, msg, fn, scope, multiline){
5440             this.show({
5441                 title : title,
5442                 msg : msg,
5443                 buttons: this.OKCANCEL,
5444                 fn: fn,
5445                 minWidth:250,
5446                 scope : scope,
5447                 prompt:true,
5448                 multiline: multiline,
5449                 modal : true
5450             });
5451             return this;
5452         },
5453
5454         /**
5455          * Button config that displays a single OK button
5456          * @type Object
5457          */
5458         OK : {ok:true},
5459         /**
5460          * Button config that displays Yes and No buttons
5461          * @type Object
5462          */
5463         YESNO : {yes:true, no:true},
5464         /**
5465          * Button config that displays OK and Cancel buttons
5466          * @type Object
5467          */
5468         OKCANCEL : {ok:true, cancel:true},
5469         /**
5470          * Button config that displays Yes, No and Cancel buttons
5471          * @type Object
5472          */
5473         YESNOCANCEL : {yes:true, no:true, cancel:true},
5474
5475         /**
5476          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5477          * @type Number
5478          */
5479         defaultTextHeight : 75,
5480         /**
5481          * The maximum width in pixels of the message box (defaults to 600)
5482          * @type Number
5483          */
5484         maxWidth : 600,
5485         /**
5486          * The minimum width in pixels of the message box (defaults to 100)
5487          * @type Number
5488          */
5489         minWidth : 100,
5490         /**
5491          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5492          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5493          * @type Number
5494          */
5495         minProgressWidth : 250,
5496         /**
5497          * An object containing the default button text strings that can be overriden for localized language support.
5498          * Supported properties are: ok, cancel, yes and no.
5499          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5500          * @type Object
5501          */
5502         buttonText : {
5503             ok : "OK",
5504             cancel : "Cancel",
5505             yes : "Yes",
5506             no : "No"
5507         }
5508     };
5509 }();
5510
5511 /**
5512  * Shorthand for {@link Roo.MessageBox}
5513  */
5514 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5515 Roo.Msg = Roo.Msg || Roo.MessageBox;
5516 /*
5517  * - LGPL
5518  *
5519  * navbar
5520  * 
5521  */
5522
5523 /**
5524  * @class Roo.bootstrap.nav.Bar
5525  * @extends Roo.bootstrap.Component
5526  * @abstract
5527  * Bootstrap Navbar class
5528
5529  * @constructor
5530  * Create a new Navbar
5531  * @param {Object} config The config object
5532  */
5533
5534
5535 Roo.bootstrap.nav.Bar = function(config){
5536     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5537     this.addEvents({
5538         // raw events
5539         /**
5540          * @event beforetoggle
5541          * Fire before toggle the menu
5542          * @param {Roo.EventObject} e
5543          */
5544         "beforetoggle" : true
5545     });
5546 };
5547
5548 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5549     
5550     
5551    
5552     // private
5553     navItems : false,
5554     loadMask : false,
5555     
5556     
5557     getAutoCreate : function(){
5558         
5559         
5560         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5561         
5562     },
5563     
5564     initEvents :function ()
5565     {
5566         //Roo.log(this.el.select('.navbar-toggle',true));
5567         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5568         
5569         var mark = {
5570             tag: "div",
5571             cls:"x-dlg-mask"
5572         };
5573         
5574         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5575         
5576         var size = this.el.getSize();
5577         this.maskEl.setSize(size.width, size.height);
5578         this.maskEl.enableDisplayMode("block");
5579         this.maskEl.hide();
5580         
5581         if(this.loadMask){
5582             this.maskEl.show();
5583         }
5584     },
5585     
5586     
5587     getChildContainer : function()
5588     {
5589         if (this.el && this.el.select('.collapse').getCount()) {
5590             return this.el.select('.collapse',true).first();
5591         }
5592         
5593         return this.el;
5594     },
5595     
5596     mask : function()
5597     {
5598         this.maskEl.show();
5599     },
5600     
5601     unmask : function()
5602     {
5603         this.maskEl.hide();
5604     },
5605     onToggle : function()
5606     {
5607         
5608         if(this.fireEvent('beforetoggle', this) === false){
5609             return;
5610         }
5611         var ce = this.el.select('.navbar-collapse',true).first();
5612       
5613         if (!ce.hasClass('show')) {
5614            this.expand();
5615         } else {
5616             this.collapse();
5617         }
5618         
5619         
5620     
5621     },
5622     /**
5623      * Expand the navbar pulldown 
5624      */
5625     expand : function ()
5626     {
5627        
5628         var ce = this.el.select('.navbar-collapse',true).first();
5629         if (ce.hasClass('collapsing')) {
5630             return;
5631         }
5632         ce.dom.style.height = '';
5633                // show it...
5634         ce.addClass('in'); // old...
5635         ce.removeClass('collapse');
5636         ce.addClass('show');
5637         var h = ce.getHeight();
5638         Roo.log(h);
5639         ce.removeClass('show');
5640         // at this point we should be able to see it..
5641         ce.addClass('collapsing');
5642         
5643         ce.setHeight(0); // resize it ...
5644         ce.on('transitionend', function() {
5645             //Roo.log('done transition');
5646             ce.removeClass('collapsing');
5647             ce.addClass('show');
5648             ce.removeClass('collapse');
5649
5650             ce.dom.style.height = '';
5651         }, this, { single: true} );
5652         ce.setHeight(h);
5653         ce.dom.scrollTop = 0;
5654     },
5655     /**
5656      * Collapse the navbar pulldown 
5657      */
5658     collapse : function()
5659     {
5660          var ce = this.el.select('.navbar-collapse',true).first();
5661        
5662         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5663             // it's collapsed or collapsing..
5664             return;
5665         }
5666         ce.removeClass('in'); // old...
5667         ce.setHeight(ce.getHeight());
5668         ce.removeClass('show');
5669         ce.addClass('collapsing');
5670         
5671         ce.on('transitionend', function() {
5672             ce.dom.style.height = '';
5673             ce.removeClass('collapsing');
5674             ce.addClass('collapse');
5675         }, this, { single: true} );
5676         ce.setHeight(0);
5677     }
5678     
5679     
5680     
5681 });
5682
5683
5684
5685  
5686
5687  /*
5688  * - LGPL
5689  *
5690  * navbar
5691  * 
5692  */
5693
5694 /**
5695  * @class Roo.bootstrap.nav.Simplebar
5696  * @extends Roo.bootstrap.nav.Bar
5697  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5698  * Bootstrap Sidebar class
5699  *
5700  * @cfg {Boolean} inverse is inverted color
5701  * 
5702  * @cfg {String} type (nav | pills | tabs)
5703  * @cfg {Boolean} arrangement stacked | justified
5704  * @cfg {String} align (left | right) alignment
5705  * 
5706  * @cfg {Boolean} main (true|false) main nav bar? default false
5707  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5708  * 
5709  * @cfg {String} tag (header|footer|nav|div) default is nav 
5710
5711  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5712  * 
5713  * 
5714  * @constructor
5715  * Create a new Sidebar
5716  * @param {Object} config The config object
5717  */
5718
5719
5720 Roo.bootstrap.nav.Simplebar = function(config){
5721     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5722 };
5723
5724 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5725     
5726     inverse: false,
5727     
5728     type: false,
5729     arrangement: '',
5730     align : false,
5731     
5732     weight : 'light',
5733     
5734     main : false,
5735     
5736     
5737     tag : false,
5738     
5739     
5740     getAutoCreate : function(){
5741         
5742         
5743         var cfg = {
5744             tag : this.tag || 'div',
5745             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5746         };
5747         if (['light','white'].indexOf(this.weight) > -1) {
5748             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5749         }
5750         cfg.cls += ' bg-' + this.weight;
5751         
5752         if (this.inverse) {
5753             cfg.cls += ' navbar-inverse';
5754             
5755         }
5756         
5757         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5758         
5759         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5760             return cfg;
5761         }
5762         
5763         
5764     
5765         
5766         cfg.cn = [
5767             {
5768                 cls: 'nav nav-' + this.xtype,
5769                 tag : 'ul'
5770             }
5771         ];
5772         
5773          
5774         this.type = this.type || 'nav';
5775         if (['tabs','pills'].indexOf(this.type) != -1) {
5776             cfg.cn[0].cls += ' nav-' + this.type
5777         
5778         
5779         } else {
5780             if (this.type!=='nav') {
5781                 Roo.log('nav type must be nav/tabs/pills')
5782             }
5783             cfg.cn[0].cls += ' navbar-nav'
5784         }
5785         
5786         
5787         
5788         
5789         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5790             cfg.cn[0].cls += ' nav-' + this.arrangement;
5791         }
5792         
5793         
5794         if (this.align === 'right') {
5795             cfg.cn[0].cls += ' navbar-right';
5796         }
5797         
5798         
5799         
5800         
5801         return cfg;
5802     
5803         
5804     }
5805     
5806     
5807     
5808 });
5809
5810
5811
5812  
5813
5814  
5815        /*
5816  * - LGPL
5817  *
5818  * navbar
5819  * navbar-fixed-top
5820  * navbar-expand-md  fixed-top 
5821  */
5822
5823 /**
5824  * @class Roo.bootstrap.nav.Headerbar
5825  * @extends Roo.bootstrap.nav.Simplebar
5826  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5827  * Bootstrap Sidebar class
5828  *
5829  * @cfg {String} brand what is brand
5830  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5831  * @cfg {String} brand_href href of the brand
5832  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5833  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5834  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5835  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5836  * 
5837  * @constructor
5838  * Create a new Sidebar
5839  * @param {Object} config The config object
5840  */
5841
5842
5843 Roo.bootstrap.nav.Headerbar = function(config){
5844     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5845       
5846 };
5847
5848 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5849     
5850     position: '',
5851     brand: '',
5852     brand_href: false,
5853     srButton : true,
5854     autohide : false,
5855     desktopCenter : false,
5856    
5857     
5858     getAutoCreate : function(){
5859         
5860         var   cfg = {
5861             tag: this.nav || 'nav',
5862             cls: 'navbar navbar-expand-md',
5863             role: 'navigation',
5864             cn: []
5865         };
5866         
5867         var cn = cfg.cn;
5868         if (this.desktopCenter) {
5869             cn.push({cls : 'container', cn : []});
5870             cn = cn[0].cn;
5871         }
5872         
5873         if(this.srButton){
5874             var btn = {
5875                 tag: 'button',
5876                 type: 'button',
5877                 cls: 'navbar-toggle navbar-toggler',
5878                 'data-toggle': 'collapse',
5879                 cn: [
5880                     {
5881                         tag: 'span',
5882                         cls: 'sr-only',
5883                         html: 'Toggle navigation'
5884                     },
5885                     {
5886                         tag: 'span',
5887                         cls: 'icon-bar navbar-toggler-icon'
5888                     },
5889                     {
5890                         tag: 'span',
5891                         cls: 'icon-bar'
5892                     },
5893                     {
5894                         tag: 'span',
5895                         cls: 'icon-bar'
5896                     }
5897                 ]
5898             };
5899             
5900             cn.push( Roo.bootstrap.version == 4 ? btn : {
5901                 tag: 'div',
5902                 cls: 'navbar-header',
5903                 cn: [
5904                     btn
5905                 ]
5906             });
5907         }
5908         
5909         cn.push({
5910             tag: 'div',
5911             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5912             cn : []
5913         });
5914         
5915         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5916         
5917         if (['light','white'].indexOf(this.weight) > -1) {
5918             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5919         }
5920         cfg.cls += ' bg-' + this.weight;
5921         
5922         
5923         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5924             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5925             
5926             // tag can override this..
5927             
5928             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5929         }
5930         
5931         if (this.brand !== '') {
5932             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5933             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5934                 tag: 'a',
5935                 href: this.brand_href ? this.brand_href : '#',
5936                 cls: 'navbar-brand',
5937                 cn: [
5938                 this.brand
5939                 ]
5940             });
5941         }
5942         
5943         if(this.main){
5944             cfg.cls += ' main-nav';
5945         }
5946         
5947         
5948         return cfg;
5949
5950         
5951     },
5952     getHeaderChildContainer : function()
5953     {
5954         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5955             return this.el.select('.navbar-header',true).first();
5956         }
5957         
5958         return this.getChildContainer();
5959     },
5960     
5961     getChildContainer : function()
5962     {
5963          
5964         return this.el.select('.roo-navbar-collapse',true).first();
5965          
5966         
5967     },
5968     
5969     initEvents : function()
5970     {
5971         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5972         
5973         if (this.autohide) {
5974             
5975             var prevScroll = 0;
5976             var ft = this.el;
5977             
5978             Roo.get(document).on('scroll',function(e) {
5979                 var ns = Roo.get(document).getScroll().top;
5980                 var os = prevScroll;
5981                 prevScroll = ns;
5982                 
5983                 if(ns > os){
5984                     ft.removeClass('slideDown');
5985                     ft.addClass('slideUp');
5986                     return;
5987                 }
5988                 ft.removeClass('slideUp');
5989                 ft.addClass('slideDown');
5990                  
5991               
5992           },this);
5993         }
5994     }    
5995     
5996 });
5997
5998
5999
6000  
6001
6002  /*
6003  * - LGPL
6004  *
6005  * navbar
6006  * 
6007  */
6008
6009 /**
6010  * @class Roo.bootstrap.nav.Sidebar
6011  * @extends Roo.bootstrap.nav.Bar
6012  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6013  * Bootstrap Sidebar class
6014  * 
6015  * @constructor
6016  * Create a new Sidebar
6017  * @param {Object} config The config object
6018  */
6019
6020
6021 Roo.bootstrap.nav.Sidebar = function(config){
6022     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6023 };
6024
6025 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6026     
6027     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6028     
6029     getAutoCreate : function(){
6030         
6031         
6032         return  {
6033             tag: 'div',
6034             cls: 'sidebar sidebar-nav'
6035         };
6036     
6037         
6038     }
6039     
6040     
6041     
6042 });
6043
6044
6045
6046  
6047
6048  /*
6049  * - LGPL
6050  *
6051  * nav group
6052  * 
6053  */
6054
6055 /**
6056  * @class Roo.bootstrap.nav.Group
6057  * @extends Roo.bootstrap.Component
6058  * @children Roo.bootstrap.nav.Item
6059  * Bootstrap NavGroup class
6060  * @cfg {String} align (left|right)
6061  * @cfg {Boolean} inverse
6062  * @cfg {String} type (nav|pills|tab) default nav
6063  * @cfg {String} navId - reference Id for navbar.
6064  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6065  * 
6066  * @constructor
6067  * Create a new nav group
6068  * @param {Object} config The config object
6069  */
6070
6071 Roo.bootstrap.nav.Group = function(config){
6072     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6073     this.navItems = [];
6074    
6075     Roo.bootstrap.nav.Group.register(this);
6076      this.addEvents({
6077         /**
6078              * @event changed
6079              * Fires when the active item changes
6080              * @param {Roo.bootstrap.nav.Group} this
6081              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6082              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6083          */
6084         'changed': true
6085      });
6086     
6087 };
6088
6089 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6090     
6091     align: '',
6092     inverse: false,
6093     form: false,
6094     type: 'nav',
6095     navId : '',
6096     // private
6097     pilltype : true,
6098     
6099     navItems : false, 
6100     
6101     getAutoCreate : function()
6102     {
6103         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6104         
6105         cfg = {
6106             tag : 'ul',
6107             cls: 'nav' 
6108         };
6109         if (Roo.bootstrap.version == 4) {
6110             if (['tabs','pills'].indexOf(this.type) != -1) {
6111                 cfg.cls += ' nav-' + this.type; 
6112             } else {
6113                 // trying to remove so header bar can right align top?
6114                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6115                     // do not use on header bar... 
6116                     cfg.cls += ' navbar-nav';
6117                 }
6118             }
6119             
6120         } else {
6121             if (['tabs','pills'].indexOf(this.type) != -1) {
6122                 cfg.cls += ' nav-' + this.type
6123             } else {
6124                 if (this.type !== 'nav') {
6125                     Roo.log('nav type must be nav/tabs/pills')
6126                 }
6127                 cfg.cls += ' navbar-nav'
6128             }
6129         }
6130         
6131         if (this.parent() && this.parent().sidebar) {
6132             cfg = {
6133                 tag: 'ul',
6134                 cls: 'dashboard-menu sidebar-menu'
6135             };
6136             
6137             return cfg;
6138         }
6139         
6140         if (this.form === true) {
6141             cfg = {
6142                 tag: 'form',
6143                 cls: 'navbar-form form-inline'
6144             };
6145             //nav navbar-right ml-md-auto
6146             if (this.align === 'right') {
6147                 cfg.cls += ' navbar-right ml-md-auto';
6148             } else {
6149                 cfg.cls += ' navbar-left';
6150             }
6151         }
6152         
6153         if (this.align === 'right') {
6154             cfg.cls += ' navbar-right ml-md-auto';
6155         } else {
6156             cfg.cls += ' mr-auto';
6157         }
6158         
6159         if (this.inverse) {
6160             cfg.cls += ' navbar-inverse';
6161             
6162         }
6163         
6164         
6165         return cfg;
6166     },
6167     /**
6168     * sets the active Navigation item
6169     * @param {Roo.bootstrap.nav.Item} the new current navitem
6170     */
6171     setActiveItem : function(item)
6172     {
6173         var prev = false;
6174         Roo.each(this.navItems, function(v){
6175             if (v == item) {
6176                 return ;
6177             }
6178             if (v.isActive()) {
6179                 v.setActive(false, true);
6180                 prev = v;
6181                 
6182             }
6183             
6184         });
6185
6186         item.setActive(true, true);
6187         this.fireEvent('changed', this, item, prev);
6188         
6189         
6190     },
6191     /**
6192     * gets the active Navigation item
6193     * @return {Roo.bootstrap.nav.Item} the current navitem
6194     */
6195     getActive : function()
6196     {
6197         
6198         var prev = false;
6199         Roo.each(this.navItems, function(v){
6200             
6201             if (v.isActive()) {
6202                 prev = v;
6203                 
6204             }
6205             
6206         });
6207         return prev;
6208     },
6209     
6210     indexOfNav : function()
6211     {
6212         
6213         var prev = false;
6214         Roo.each(this.navItems, function(v,i){
6215             
6216             if (v.isActive()) {
6217                 prev = i;
6218                 
6219             }
6220             
6221         });
6222         return prev;
6223     },
6224     /**
6225     * adds a Navigation item
6226     * @param {Roo.bootstrap.nav.Item} the navitem to add
6227     */
6228     addItem : function(cfg)
6229     {
6230         if (this.form && Roo.bootstrap.version == 4) {
6231             cfg.tag = 'div';
6232         }
6233         var cn = new Roo.bootstrap.nav.Item(cfg);
6234         this.register(cn);
6235         cn.parentId = this.id;
6236         cn.onRender(this.el, null);
6237         return cn;
6238     },
6239     /**
6240     * register a Navigation item
6241     * @param {Roo.bootstrap.nav.Item} the navitem to add
6242     */
6243     register : function(item)
6244     {
6245         this.navItems.push( item);
6246         item.navId = this.navId;
6247     
6248     },
6249     
6250     /**
6251     * clear all the Navigation item
6252     */
6253    
6254     clearAll : function()
6255     {
6256         this.navItems = [];
6257         this.el.dom.innerHTML = '';
6258     },
6259     
6260     getNavItem: function(tabId)
6261     {
6262         var ret = false;
6263         Roo.each(this.navItems, function(e) {
6264             if (e.tabId == tabId) {
6265                ret =  e;
6266                return false;
6267             }
6268             return true;
6269             
6270         });
6271         return ret;
6272     },
6273     
6274     setActiveNext : function()
6275     {
6276         var i = this.indexOfNav(this.getActive());
6277         if (i > this.navItems.length) {
6278             return;
6279         }
6280         this.setActiveItem(this.navItems[i+1]);
6281     },
6282     setActivePrev : function()
6283     {
6284         var i = this.indexOfNav(this.getActive());
6285         if (i  < 1) {
6286             return;
6287         }
6288         this.setActiveItem(this.navItems[i-1]);
6289     },
6290     clearWasActive : function(except) {
6291         Roo.each(this.navItems, function(e) {
6292             if (e.tabId != except.tabId && e.was_active) {
6293                e.was_active = false;
6294                return false;
6295             }
6296             return true;
6297             
6298         });
6299     },
6300     getWasActive : function ()
6301     {
6302         var r = false;
6303         Roo.each(this.navItems, function(e) {
6304             if (e.was_active) {
6305                r = e;
6306                return false;
6307             }
6308             return true;
6309             
6310         });
6311         return r;
6312     }
6313     
6314     
6315 });
6316
6317  
6318 Roo.apply(Roo.bootstrap.nav.Group, {
6319     
6320     groups: {},
6321      /**
6322     * register a Navigation Group
6323     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6324     */
6325     register : function(navgrp)
6326     {
6327         this.groups[navgrp.navId] = navgrp;
6328         
6329     },
6330     /**
6331     * fetch a Navigation Group based on the navigation ID
6332     * @param {string} the navgroup to add
6333     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6334     */
6335     get: function(navId) {
6336         if (typeof(this.groups[navId]) == 'undefined') {
6337             return false;
6338             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6339         }
6340         return this.groups[navId] ;
6341     }
6342     
6343     
6344     
6345 });
6346
6347  /**
6348  * @class Roo.bootstrap.nav.Item
6349  * @extends Roo.bootstrap.Component
6350  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6351  * @parent Roo.bootstrap.nav.Group
6352  * @licence LGPL
6353  * Bootstrap Navbar.NavItem class
6354  * 
6355  * @cfg {String} href  link to
6356  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6357  * @cfg {Boolean} button_outline show and outlined button
6358  * @cfg {String} html content of button
6359  * @cfg {String} badge text inside badge
6360  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6361  * @cfg {String} glyphicon DEPRICATED - use fa
6362  * @cfg {String} icon DEPRICATED - use fa
6363  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6364  * @cfg {Boolean} active Is item active
6365  * @cfg {Boolean} disabled Is item disabled
6366  * @cfg {String} linkcls  Link Class
6367  * @cfg {Boolean} preventDefault (true | false) default false
6368  * @cfg {String} tabId the tab that this item activates.
6369  * @cfg {String} tagtype (a|span) render as a href or span?
6370  * @cfg {Boolean} animateRef (true|false) link to element default false  
6371  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6372   
6373  * @constructor
6374  * Create a new Navbar Item
6375  * @param {Object} config The config object
6376  */
6377 Roo.bootstrap.nav.Item = function(config){
6378     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6379     this.addEvents({
6380         // raw events
6381         /**
6382          * @event click
6383          * The raw click event for the entire grid.
6384          * @param {Roo.EventObject} e
6385          */
6386         "click" : true,
6387          /**
6388             * @event changed
6389             * Fires when the active item active state changes
6390             * @param {Roo.bootstrap.nav.Item} this
6391             * @param {boolean} state the new state
6392              
6393          */
6394         'changed': true,
6395         /**
6396             * @event scrollto
6397             * Fires when scroll to element
6398             * @param {Roo.bootstrap.nav.Item} this
6399             * @param {Object} options
6400             * @param {Roo.EventObject} e
6401              
6402          */
6403         'scrollto': true
6404     });
6405    
6406 };
6407
6408 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6409     
6410     href: false,
6411     html: '',
6412     badge: '',
6413     icon: false,
6414     fa : false,
6415     glyphicon: false,
6416     active: false,
6417     preventDefault : false,
6418     tabId : false,
6419     tagtype : 'a',
6420     tag: 'li',
6421     disabled : false,
6422     animateRef : false,
6423     was_active : false,
6424     button_weight : '',
6425     button_outline : false,
6426     linkcls : '',
6427     navLink: false,
6428     
6429     getAutoCreate : function(){
6430          
6431         var cfg = {
6432             tag: this.tag,
6433             cls: 'nav-item'
6434         };
6435         
6436         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6437         
6438         if (this.active) {
6439             cfg.cls +=  ' active' ;
6440         }
6441         if (this.disabled) {
6442             cfg.cls += ' disabled';
6443         }
6444         
6445         // BS4 only?
6446         if (this.button_weight.length) {
6447             cfg.tag = this.href ? 'a' : 'button';
6448             cfg.html = this.html || '';
6449             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6450             if (this.href) {
6451                 cfg.href = this.href;
6452             }
6453             if (this.fa) {
6454                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6455             } else {
6456                 cfg.cls += " nav-html";
6457             }
6458             
6459             // menu .. should add dropdown-menu class - so no need for carat..
6460             
6461             if (this.badge !== '') {
6462                  
6463                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6464             }
6465             return cfg;
6466         }
6467         
6468         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6469             cfg.cn = [
6470                 {
6471                     tag: this.tagtype,
6472                     href : this.href || "#",
6473                     html: this.html || '',
6474                     cls : ''
6475                 }
6476             ];
6477             if (this.tagtype == 'a') {
6478                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6479         
6480             }
6481             if (this.icon) {
6482                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6483             } else  if (this.fa) {
6484                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6485             } else if(this.glyphicon) {
6486                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6487             } else {
6488                 cfg.cn[0].cls += " nav-html";
6489             }
6490             
6491             if (this.menu) {
6492                 cfg.cn[0].html += " <span class='caret'></span>";
6493              
6494             }
6495             
6496             if (this.badge !== '') {
6497                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6498             }
6499         }
6500         
6501         
6502         
6503         return cfg;
6504     },
6505     onRender : function(ct, position)
6506     {
6507        // Roo.log("Call onRender: " + this.xtype);
6508         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6509             this.tag = 'div';
6510         }
6511         
6512         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6513         this.navLink = this.el.select('.nav-link',true).first();
6514         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6515         return ret;
6516     },
6517       
6518     
6519     initEvents: function() 
6520     {
6521         if (typeof (this.menu) != 'undefined') {
6522             this.menu.parentType = this.xtype;
6523             this.menu.triggerEl = this.el;
6524             this.menu = this.addxtype(Roo.apply({}, this.menu));
6525         }
6526         
6527         this.el.on('click', this.onClick, this);
6528         
6529         //if(this.tagtype == 'span'){
6530         //    this.el.select('span',true).on('click', this.onClick, this);
6531         //}
6532        
6533         // at this point parent should be available..
6534         this.parent().register(this);
6535     },
6536     
6537     onClick : function(e)
6538     {
6539         if (e.getTarget('.dropdown-menu-item')) {
6540             // did you click on a menu itemm.... - then don't trigger onclick..
6541             return;
6542         }
6543         
6544         if(
6545                 this.preventDefault || 
6546                 this.href == '#' 
6547         ){
6548             Roo.log("NavItem - prevent Default?");
6549             e.preventDefault();
6550         }
6551         
6552         if (this.disabled) {
6553             return;
6554         }
6555         
6556         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6557         if (tg && tg.transition) {
6558             Roo.log("waiting for the transitionend");
6559             return;
6560         }
6561         
6562         
6563         
6564         //Roo.log("fire event clicked");
6565         if(this.fireEvent('click', this, e) === false){
6566             return;
6567         };
6568         
6569         if(this.tagtype == 'span'){
6570             return;
6571         }
6572         
6573         //Roo.log(this.href);
6574         var ael = this.el.select('a',true).first();
6575         //Roo.log(ael);
6576         
6577         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6578             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6579             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6580                 return; // ignore... - it's a 'hash' to another page.
6581             }
6582             Roo.log("NavItem - prevent Default?");
6583             e.preventDefault();
6584             this.scrollToElement(e);
6585         }
6586         
6587         
6588         var p =  this.parent();
6589    
6590         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6591             if (typeof(p.setActiveItem) !== 'undefined') {
6592                 p.setActiveItem(this);
6593             }
6594         }
6595         
6596         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6597         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6598             // remove the collapsed menu expand...
6599             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6600         }
6601     },
6602     
6603     isActive: function () {
6604         return this.active
6605     },
6606     setActive : function(state, fire, is_was_active)
6607     {
6608         if (this.active && !state && this.navId) {
6609             this.was_active = true;
6610             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6611             if (nv) {
6612                 nv.clearWasActive(this);
6613             }
6614             
6615         }
6616         this.active = state;
6617         
6618         if (!state ) {
6619             this.el.removeClass('active');
6620             this.navLink ? this.navLink.removeClass('active') : false;
6621         } else if (!this.el.hasClass('active')) {
6622             
6623             this.el.addClass('active');
6624             if (Roo.bootstrap.version == 4 && this.navLink ) {
6625                 this.navLink.addClass('active');
6626             }
6627             
6628         }
6629         if (fire) {
6630             this.fireEvent('changed', this, state);
6631         }
6632         
6633         // show a panel if it's registered and related..
6634         
6635         if (!this.navId || !this.tabId || !state || is_was_active) {
6636             return;
6637         }
6638         
6639         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6640         if (!tg) {
6641             return;
6642         }
6643         var pan = tg.getPanelByName(this.tabId);
6644         if (!pan) {
6645             return;
6646         }
6647         // if we can not flip to new panel - go back to old nav highlight..
6648         if (false == tg.showPanel(pan)) {
6649             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6650             if (nv) {
6651                 var onav = nv.getWasActive();
6652                 if (onav) {
6653                     onav.setActive(true, false, true);
6654                 }
6655             }
6656             
6657         }
6658         
6659         
6660         
6661     },
6662      // this should not be here...
6663     setDisabled : function(state)
6664     {
6665         this.disabled = state;
6666         if (!state ) {
6667             this.el.removeClass('disabled');
6668         } else if (!this.el.hasClass('disabled')) {
6669             this.el.addClass('disabled');
6670         }
6671         
6672     },
6673     
6674     /**
6675      * Fetch the element to display the tooltip on.
6676      * @return {Roo.Element} defaults to this.el
6677      */
6678     tooltipEl : function()
6679     {
6680         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6681     },
6682     
6683     scrollToElement : function(e)
6684     {
6685         var c = document.body;
6686         
6687         /*
6688          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6689          */
6690         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6691             c = document.documentElement;
6692         }
6693         
6694         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6695         
6696         if(!target){
6697             return;
6698         }
6699
6700         var o = target.calcOffsetsTo(c);
6701         
6702         var options = {
6703             target : target,
6704             value : o[1]
6705         };
6706         
6707         this.fireEvent('scrollto', this, options, e);
6708         
6709         Roo.get(c).scrollTo('top', options.value, true);
6710         
6711         return;
6712     },
6713     /**
6714      * Set the HTML (text content) of the item
6715      * @param {string} html  content for the nav item
6716      */
6717     setHtml : function(html)
6718     {
6719         this.html = html;
6720         this.htmlEl.dom.innerHTML = html;
6721         
6722     } 
6723 });
6724  
6725
6726  /*
6727  * - LGPL
6728  *
6729  * sidebar item
6730  *
6731  *  li
6732  *    <span> icon </span>
6733  *    <span> text </span>
6734  *    <span>badge </span>
6735  */
6736
6737 /**
6738  * @class Roo.bootstrap.nav.SidebarItem
6739  * @extends Roo.bootstrap.nav.Item
6740  * Bootstrap Navbar.NavSidebarItem class
6741  * 
6742  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6743  * {Boolean} open is the menu open
6744  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6745  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6746  * {String} buttonSize (sm|md|lg)the extra classes for the button
6747  * {Boolean} showArrow show arrow next to the text (default true)
6748  * @constructor
6749  * Create a new Navbar Button
6750  * @param {Object} config The config object
6751  */
6752 Roo.bootstrap.nav.SidebarItem = function(config){
6753     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6754     this.addEvents({
6755         // raw events
6756         /**
6757          * @event click
6758          * The raw click event for the entire grid.
6759          * @param {Roo.EventObject} e
6760          */
6761         "click" : true,
6762          /**
6763             * @event changed
6764             * Fires when the active item active state changes
6765             * @param {Roo.bootstrap.nav.SidebarItem} this
6766             * @param {boolean} state the new state
6767              
6768          */
6769         'changed': true
6770     });
6771    
6772 };
6773
6774 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6775     
6776     badgeWeight : 'default',
6777     
6778     open: false,
6779     
6780     buttonView : false,
6781     
6782     buttonWeight : 'default',
6783     
6784     buttonSize : 'md',
6785     
6786     showArrow : true,
6787     
6788     getAutoCreate : function(){
6789         
6790         
6791         var a = {
6792                 tag: 'a',
6793                 href : this.href || '#',
6794                 cls: '',
6795                 html : '',
6796                 cn : []
6797         };
6798         
6799         if(this.buttonView){
6800             a = {
6801                 tag: 'button',
6802                 href : this.href || '#',
6803                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6804                 html : this.html,
6805                 cn : []
6806             };
6807         }
6808         
6809         var cfg = {
6810             tag: 'li',
6811             cls: '',
6812             cn: [ a ]
6813         };
6814         
6815         if (this.active) {
6816             cfg.cls += ' active';
6817         }
6818         
6819         if (this.disabled) {
6820             cfg.cls += ' disabled';
6821         }
6822         if (this.open) {
6823             cfg.cls += ' open x-open';
6824         }
6825         // left icon..
6826         if (this.glyphicon || this.icon) {
6827             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6828             a.cn.push({ tag : 'i', cls : c }) ;
6829         }
6830         
6831         if(!this.buttonView){
6832             var span = {
6833                 tag: 'span',
6834                 html : this.html || ''
6835             };
6836
6837             a.cn.push(span);
6838             
6839         }
6840         
6841         if (this.badge !== '') {
6842             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6843         }
6844         
6845         if (this.menu) {
6846             
6847             if(this.showArrow){
6848                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6849             }
6850             
6851             a.cls += ' dropdown-toggle treeview' ;
6852         }
6853         
6854         return cfg;
6855     },
6856     
6857     initEvents : function()
6858     { 
6859         if (typeof (this.menu) != 'undefined') {
6860             this.menu.parentType = this.xtype;
6861             this.menu.triggerEl = this.el;
6862             this.menu = this.addxtype(Roo.apply({}, this.menu));
6863         }
6864         
6865         this.el.on('click', this.onClick, this);
6866         
6867         if(this.badge !== ''){
6868             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6869         }
6870         
6871     },
6872     
6873     onClick : function(e)
6874     {
6875         if(this.disabled){
6876             e.preventDefault();
6877             return;
6878         }
6879         
6880         if(this.preventDefault){
6881             e.preventDefault();
6882         }
6883         
6884         this.fireEvent('click', this, e);
6885     },
6886     
6887     disable : function()
6888     {
6889         this.setDisabled(true);
6890     },
6891     
6892     enable : function()
6893     {
6894         this.setDisabled(false);
6895     },
6896     
6897     setDisabled : function(state)
6898     {
6899         if(this.disabled == state){
6900             return;
6901         }
6902         
6903         this.disabled = state;
6904         
6905         if (state) {
6906             this.el.addClass('disabled');
6907             return;
6908         }
6909         
6910         this.el.removeClass('disabled');
6911         
6912         return;
6913     },
6914     
6915     setActive : function(state)
6916     {
6917         if(this.active == state){
6918             return;
6919         }
6920         
6921         this.active = state;
6922         
6923         if (state) {
6924             this.el.addClass('active');
6925             return;
6926         }
6927         
6928         this.el.removeClass('active');
6929         
6930         return;
6931     },
6932     
6933     isActive: function () 
6934     {
6935         return this.active;
6936     },
6937     
6938     setBadge : function(str)
6939     {
6940         if(!this.badgeEl){
6941             return;
6942         }
6943         
6944         this.badgeEl.dom.innerHTML = str;
6945     }
6946     
6947    
6948      
6949  
6950 });
6951  
6952
6953  /*
6954  * - LGPL
6955  *
6956  * nav progress bar
6957  * 
6958  */
6959
6960 /**
6961  * @class Roo.bootstrap.nav.ProgressBar
6962  * @extends Roo.bootstrap.Component
6963  * @children Roo.bootstrap.nav.ProgressBarItem
6964  * Bootstrap NavProgressBar class
6965  * 
6966  * @constructor
6967  * Create a new nav progress bar - a bar indicating step along a process
6968  * @param {Object} config The config object
6969  */
6970
6971 Roo.bootstrap.nav.ProgressBar = function(config){
6972     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6973
6974     this.bullets = this.bullets || [];
6975    
6976 //    Roo.bootstrap.nav.ProgressBar.register(this);
6977      this.addEvents({
6978         /**
6979              * @event changed
6980              * Fires when the active item changes
6981              * @param {Roo.bootstrap.nav.ProgressBar} this
6982              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6983              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6984          */
6985         'changed': true
6986      });
6987     
6988 };
6989
6990 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
6991     /**
6992      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
6993      * Bullets for the Nav Progress bar for the toolbar
6994      */
6995     bullets : [],
6996     barItems : [],
6997     
6998     getAutoCreate : function()
6999     {
7000         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7001         
7002         cfg = {
7003             tag : 'div',
7004             cls : 'roo-navigation-bar-group',
7005             cn : [
7006                 {
7007                     tag : 'div',
7008                     cls : 'roo-navigation-top-bar'
7009                 },
7010                 {
7011                     tag : 'div',
7012                     cls : 'roo-navigation-bullets-bar',
7013                     cn : [
7014                         {
7015                             tag : 'ul',
7016                             cls : 'roo-navigation-bar'
7017                         }
7018                     ]
7019                 },
7020                 
7021                 {
7022                     tag : 'div',
7023                     cls : 'roo-navigation-bottom-bar'
7024                 }
7025             ]
7026             
7027         };
7028         
7029         return cfg;
7030         
7031     },
7032     
7033     initEvents: function() 
7034     {
7035         
7036     },
7037     
7038     onRender : function(ct, position) 
7039     {
7040         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7041         
7042         if(this.bullets.length){
7043             Roo.each(this.bullets, function(b){
7044                this.addItem(b);
7045             }, this);
7046         }
7047         
7048         this.format();
7049         
7050     },
7051     
7052     addItem : function(cfg)
7053     {
7054         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7055         
7056         item.parentId = this.id;
7057         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7058         
7059         if(cfg.html){
7060             var top = new Roo.bootstrap.Element({
7061                 tag : 'div',
7062                 cls : 'roo-navigation-bar-text'
7063             });
7064             
7065             var bottom = new Roo.bootstrap.Element({
7066                 tag : 'div',
7067                 cls : 'roo-navigation-bar-text'
7068             });
7069             
7070             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7071             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7072             
7073             var topText = new Roo.bootstrap.Element({
7074                 tag : 'span',
7075                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7076             });
7077             
7078             var bottomText = new Roo.bootstrap.Element({
7079                 tag : 'span',
7080                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7081             });
7082             
7083             topText.onRender(top.el, null);
7084             bottomText.onRender(bottom.el, null);
7085             
7086             item.topEl = top;
7087             item.bottomEl = bottom;
7088         }
7089         
7090         this.barItems.push(item);
7091         
7092         return item;
7093     },
7094     
7095     getActive : function()
7096     {
7097         var active = false;
7098         
7099         Roo.each(this.barItems, function(v){
7100             
7101             if (!v.isActive()) {
7102                 return;
7103             }
7104             
7105             active = v;
7106             return false;
7107             
7108         });
7109         
7110         return active;
7111     },
7112     
7113     setActiveItem : function(item)
7114     {
7115         var prev = false;
7116         
7117         Roo.each(this.barItems, function(v){
7118             if (v.rid == item.rid) {
7119                 return ;
7120             }
7121             
7122             if (v.isActive()) {
7123                 v.setActive(false);
7124                 prev = v;
7125             }
7126         });
7127
7128         item.setActive(true);
7129         
7130         this.fireEvent('changed', this, item, prev);
7131     },
7132     
7133     getBarItem: function(rid)
7134     {
7135         var ret = false;
7136         
7137         Roo.each(this.barItems, function(e) {
7138             if (e.rid != rid) {
7139                 return;
7140             }
7141             
7142             ret =  e;
7143             return false;
7144         });
7145         
7146         return ret;
7147     },
7148     
7149     indexOfItem : function(item)
7150     {
7151         var index = false;
7152         
7153         Roo.each(this.barItems, function(v, i){
7154             
7155             if (v.rid != item.rid) {
7156                 return;
7157             }
7158             
7159             index = i;
7160             return false
7161         });
7162         
7163         return index;
7164     },
7165     
7166     setActiveNext : function()
7167     {
7168         var i = this.indexOfItem(this.getActive());
7169         
7170         if (i > this.barItems.length) {
7171             return;
7172         }
7173         
7174         this.setActiveItem(this.barItems[i+1]);
7175     },
7176     
7177     setActivePrev : function()
7178     {
7179         var i = this.indexOfItem(this.getActive());
7180         
7181         if (i  < 1) {
7182             return;
7183         }
7184         
7185         this.setActiveItem(this.barItems[i-1]);
7186     },
7187     
7188     format : function()
7189     {
7190         if(!this.barItems.length){
7191             return;
7192         }
7193      
7194         var width = 100 / this.barItems.length;
7195         
7196         Roo.each(this.barItems, function(i){
7197             i.el.setStyle('width', width + '%');
7198             i.topEl.el.setStyle('width', width + '%');
7199             i.bottomEl.el.setStyle('width', width + '%');
7200         }, this);
7201         
7202     }
7203     
7204 });
7205 /*
7206  * - LGPL
7207  *
7208  * Nav Progress Item
7209  * 
7210  */
7211
7212 /**
7213  * @class Roo.bootstrap.nav.ProgressBarItem
7214  * @extends Roo.bootstrap.Component
7215  * Bootstrap NavProgressBarItem class
7216  * @cfg {String} rid the reference id
7217  * @cfg {Boolean} active (true|false) Is item active default false
7218  * @cfg {Boolean} disabled (true|false) Is item active default false
7219  * @cfg {String} html
7220  * @cfg {String} position (top|bottom) text position default bottom
7221  * @cfg {String} icon show icon instead of number
7222  * 
7223  * @constructor
7224  * Create a new NavProgressBarItem
7225  * @param {Object} config The config object
7226  */
7227 Roo.bootstrap.nav.ProgressBarItem = function(config){
7228     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7229     this.addEvents({
7230         // raw events
7231         /**
7232          * @event click
7233          * The raw click event for the entire grid.
7234          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7235          * @param {Roo.EventObject} e
7236          */
7237         "click" : true
7238     });
7239    
7240 };
7241
7242 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7243     
7244     rid : '',
7245     active : false,
7246     disabled : false,
7247     html : '',
7248     position : 'bottom',
7249     icon : false,
7250     
7251     getAutoCreate : function()
7252     {
7253         var iconCls = 'roo-navigation-bar-item-icon';
7254         
7255         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7256         
7257         var cfg = {
7258             tag: 'li',
7259             cls: 'roo-navigation-bar-item',
7260             cn : [
7261                 {
7262                     tag : 'i',
7263                     cls : iconCls
7264                 }
7265             ]
7266         };
7267         
7268         if(this.active){
7269             cfg.cls += ' active';
7270         }
7271         if(this.disabled){
7272             cfg.cls += ' disabled';
7273         }
7274         
7275         return cfg;
7276     },
7277     
7278     disable : function()
7279     {
7280         this.setDisabled(true);
7281     },
7282     
7283     enable : function()
7284     {
7285         this.setDisabled(false);
7286     },
7287     
7288     initEvents: function() 
7289     {
7290         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7291         
7292         this.iconEl.on('click', this.onClick, this);
7293     },
7294     
7295     onClick : function(e)
7296     {
7297         e.preventDefault();
7298         
7299         if(this.disabled){
7300             return;
7301         }
7302         
7303         if(this.fireEvent('click', this, e) === false){
7304             return;
7305         };
7306         
7307         this.parent().setActiveItem(this);
7308     },
7309     
7310     isActive: function () 
7311     {
7312         return this.active;
7313     },
7314     
7315     setActive : function(state)
7316     {
7317         if(this.active == state){
7318             return;
7319         }
7320         
7321         this.active = state;
7322         
7323         if (state) {
7324             this.el.addClass('active');
7325             return;
7326         }
7327         
7328         this.el.removeClass('active');
7329         
7330         return;
7331     },
7332     
7333     setDisabled : function(state)
7334     {
7335         if(this.disabled == state){
7336             return;
7337         }
7338         
7339         this.disabled = state;
7340         
7341         if (state) {
7342             this.el.addClass('disabled');
7343             return;
7344         }
7345         
7346         this.el.removeClass('disabled');
7347     },
7348     
7349     tooltipEl : function()
7350     {
7351         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7352     }
7353 });
7354  
7355
7356  /*
7357  * - LGPL
7358  *
7359  *  Breadcrumb Nav
7360  * 
7361  */
7362 Roo.namespace('Roo.bootstrap.breadcrumb');
7363
7364
7365 /**
7366  * @class Roo.bootstrap.breadcrumb.Nav
7367  * @extends Roo.bootstrap.Component
7368  * Bootstrap Breadcrumb Nav Class
7369  *  
7370  * @children Roo.bootstrap.breadcrumb.Item
7371  * 
7372  * @constructor
7373  * Create a new breadcrumb.Nav
7374  * @param {Object} config The config object
7375  */
7376
7377
7378 Roo.bootstrap.breadcrumb.Nav = function(config){
7379     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7380     
7381     
7382 };
7383
7384 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7385     
7386     getAutoCreate : function()
7387     {
7388
7389         var cfg = {
7390             tag: 'nav',
7391             cn : [
7392                 {
7393                     tag : 'ol',
7394                     cls : 'breadcrumb'
7395                 }
7396             ]
7397             
7398         };
7399           
7400         return cfg;
7401     },
7402     
7403     initEvents: function()
7404     {
7405         this.olEl = this.el.select('ol',true).first();    
7406     },
7407     getChildContainer : function()
7408     {
7409         return this.olEl;  
7410     }
7411     
7412 });
7413
7414  /*
7415  * - LGPL
7416  *
7417  *  Breadcrumb Item
7418  * 
7419  */
7420
7421
7422 /**
7423  * @class Roo.bootstrap.breadcrumb.Nav
7424  * @extends Roo.bootstrap.Component
7425  * @children Roo.bootstrap.Component
7426  * @parent Roo.bootstrap.breadcrumb.Nav
7427  * Bootstrap Breadcrumb Nav Class
7428  *  
7429  * 
7430  * @cfg {String} html the content of the link.
7431  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7432  * @cfg {Boolean} active is it active
7433
7434  * 
7435  * @constructor
7436  * Create a new breadcrumb.Nav
7437  * @param {Object} config The config object
7438  */
7439
7440 Roo.bootstrap.breadcrumb.Item = function(config){
7441     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7442     this.addEvents({
7443         // img events
7444         /**
7445          * @event click
7446          * The img click event for the img.
7447          * @param {Roo.EventObject} e
7448          */
7449         "click" : true
7450     });
7451     
7452 };
7453
7454 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7455     
7456     href: false,
7457     html : '',
7458     
7459     getAutoCreate : function()
7460     {
7461
7462         var cfg = {
7463             tag: 'li',
7464             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7465         };
7466         if (this.href !== false) {
7467             cfg.cn = [{
7468                 tag : 'a',
7469                 href : this.href,
7470                 html : this.html
7471             }];
7472         } else {
7473             cfg.html = this.html;
7474         }
7475         
7476         return cfg;
7477     },
7478     
7479     initEvents: function()
7480     {
7481         if (this.href) {
7482             this.el.select('a', true).first().on('click',this.onClick, this)
7483         }
7484         
7485     },
7486     onClick : function(e)
7487     {
7488         e.preventDefault();
7489         this.fireEvent('click',this,  e);
7490     }
7491     
7492 });
7493
7494  /*
7495  * - LGPL
7496  *
7497  * row
7498  * 
7499  */
7500
7501 /**
7502  * @class Roo.bootstrap.Row
7503  * @extends Roo.bootstrap.Component
7504  * @children Roo.bootstrap.Component
7505  * Bootstrap Row class (contains columns...)
7506  * 
7507  * @constructor
7508  * Create a new Row
7509  * @param {Object} config The config object
7510  */
7511
7512 Roo.bootstrap.Row = function(config){
7513     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7514 };
7515
7516 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7517     
7518     getAutoCreate : function(){
7519        return {
7520             cls: 'row clearfix'
7521        };
7522     }
7523     
7524     
7525 });
7526
7527  
7528
7529  /*
7530  * - LGPL
7531  *
7532  * pagination
7533  * 
7534  */
7535
7536 /**
7537  * @class Roo.bootstrap.Pagination
7538  * @extends Roo.bootstrap.Component
7539  * @children Roo.bootstrap.Pagination
7540  * Bootstrap Pagination class
7541  * 
7542  * @cfg {String} size (xs|sm|md|lg|xl)
7543  * @cfg {Boolean} inverse 
7544  * 
7545  * @constructor
7546  * Create a new Pagination
7547  * @param {Object} config The config object
7548  */
7549
7550 Roo.bootstrap.Pagination = function(config){
7551     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7552 };
7553
7554 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7555     
7556     cls: false,
7557     size: false,
7558     inverse: false,
7559     
7560     getAutoCreate : function(){
7561         var cfg = {
7562             tag: 'ul',
7563                 cls: 'pagination'
7564         };
7565         if (this.inverse) {
7566             cfg.cls += ' inverse';
7567         }
7568         if (this.html) {
7569             cfg.html=this.html;
7570         }
7571         if (this.cls) {
7572             cfg.cls += " " + this.cls;
7573         }
7574         return cfg;
7575     }
7576    
7577 });
7578
7579  
7580
7581  /*
7582  * - LGPL
7583  *
7584  * Pagination item
7585  * 
7586  */
7587
7588
7589 /**
7590  * @class Roo.bootstrap.PaginationItem
7591  * @extends Roo.bootstrap.Component
7592  * Bootstrap PaginationItem class
7593  * @cfg {String} html text
7594  * @cfg {String} href the link
7595  * @cfg {Boolean} preventDefault (true | false) default true
7596  * @cfg {Boolean} active (true | false) default false
7597  * @cfg {Boolean} disabled default false
7598  * 
7599  * 
7600  * @constructor
7601  * Create a new PaginationItem
7602  * @param {Object} config The config object
7603  */
7604
7605
7606 Roo.bootstrap.PaginationItem = function(config){
7607     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7608     this.addEvents({
7609         // raw events
7610         /**
7611          * @event click
7612          * The raw click event for the entire grid.
7613          * @param {Roo.EventObject} e
7614          */
7615         "click" : true
7616     });
7617 };
7618
7619 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7620     
7621     href : false,
7622     html : false,
7623     preventDefault: true,
7624     active : false,
7625     cls : false,
7626     disabled: false,
7627     
7628     getAutoCreate : function(){
7629         var cfg= {
7630             tag: 'li',
7631             cn: [
7632                 {
7633                     tag : 'a',
7634                     href : this.href ? this.href : '#',
7635                     html : this.html ? this.html : ''
7636                 }
7637             ]
7638         };
7639         
7640         if(this.cls){
7641             cfg.cls = this.cls;
7642         }
7643         
7644         if(this.disabled){
7645             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7646         }
7647         
7648         if(this.active){
7649             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7650         }
7651         
7652         return cfg;
7653     },
7654     
7655     initEvents: function() {
7656         
7657         this.el.on('click', this.onClick, this);
7658         
7659     },
7660     onClick : function(e)
7661     {
7662         Roo.log('PaginationItem on click ');
7663         if(this.preventDefault){
7664             e.preventDefault();
7665         }
7666         
7667         if(this.disabled){
7668             return;
7669         }
7670         
7671         this.fireEvent('click', this, e);
7672     }
7673    
7674 });
7675
7676  
7677
7678  /*
7679  * - LGPL
7680  *
7681  * slider
7682  * 
7683  */
7684
7685
7686 /**
7687  * @class Roo.bootstrap.Slider
7688  * @extends Roo.bootstrap.Component
7689  * Bootstrap Slider class
7690  *    
7691  * @constructor
7692  * Create a new Slider
7693  * @param {Object} config The config object
7694  */
7695
7696 Roo.bootstrap.Slider = function(config){
7697     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7698 };
7699
7700 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7701     
7702     getAutoCreate : function(){
7703         
7704         var cfg = {
7705             tag: 'div',
7706             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7707             cn: [
7708                 {
7709                     tag: 'a',
7710                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7711                 }
7712             ]
7713         };
7714         
7715         return cfg;
7716     }
7717    
7718 });
7719
7720  /*
7721  * Based on:
7722  * Ext JS Library 1.1.1
7723  * Copyright(c) 2006-2007, Ext JS, LLC.
7724  *
7725  * Originally Released Under LGPL - original licence link has changed is not relivant.
7726  *
7727  * Fork - LGPL
7728  * <script type="text/javascript">
7729  */
7730  /**
7731  * @extends Roo.dd.DDProxy
7732  * @class Roo.grid.SplitDragZone
7733  * Support for Column Header resizing
7734  * @constructor
7735  * @param {Object} config
7736  */
7737 // private
7738 // This is a support class used internally by the Grid components
7739 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7740     this.grid = grid;
7741     this.view = grid.getView();
7742     this.proxy = this.view.resizeProxy;
7743     Roo.grid.SplitDragZone.superclass.constructor.call(
7744         this,
7745         hd, // ID
7746         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7747         {  // CONFIG
7748             dragElId : Roo.id(this.proxy.dom),
7749             resizeFrame:false
7750         }
7751     );
7752     
7753     this.setHandleElId(Roo.id(hd));
7754     if (hd2 !== false) {
7755         this.setOuterHandleElId(Roo.id(hd2));
7756     }
7757     
7758     this.scroll = false;
7759 };
7760 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7761     fly: Roo.Element.fly,
7762
7763     b4StartDrag : function(x, y){
7764         this.view.headersDisabled = true;
7765         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7766                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7767         );
7768         this.proxy.setHeight(h);
7769         
7770         // for old system colWidth really stored the actual width?
7771         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7772         // which in reality did not work.. - it worked only for fixed sizes
7773         // for resizable we need to use actual sizes.
7774         var w = this.cm.getColumnWidth(this.cellIndex);
7775         if (!this.view.mainWrap) {
7776             // bootstrap.
7777             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7778         }
7779         
7780         
7781         
7782         // this was w-this.grid.minColumnWidth;
7783         // doesnt really make sense? - w = thie curren width or the rendered one?
7784         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7785         this.resetConstraints();
7786         this.setXConstraint(minw, 1000);
7787         this.setYConstraint(0, 0);
7788         this.minX = x - minw;
7789         this.maxX = x + 1000;
7790         this.startPos = x;
7791         if (!this.view.mainWrap) { // this is Bootstrap code..
7792             this.getDragEl().style.display='block';
7793         }
7794         
7795         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7796     },
7797
7798
7799     handleMouseDown : function(e){
7800         ev = Roo.EventObject.setEvent(e);
7801         var t = this.fly(ev.getTarget());
7802         if(t.hasClass("x-grid-split")){
7803             this.cellIndex = this.view.getCellIndex(t.dom);
7804             this.split = t.dom;
7805             this.cm = this.grid.colModel;
7806             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7807                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7808             }
7809         }
7810     },
7811
7812     endDrag : function(e){
7813         this.view.headersDisabled = false;
7814         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7815         var diff = endX - this.startPos;
7816         // 
7817         var w = this.cm.getColumnWidth(this.cellIndex);
7818         if (!this.view.mainWrap) {
7819             w = 0;
7820         }
7821         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7822     },
7823
7824     autoOffset : function(){
7825         this.setDelta(0,0);
7826     }
7827 });/*
7828  * Based on:
7829  * Ext JS Library 1.1.1
7830  * Copyright(c) 2006-2007, Ext JS, LLC.
7831  *
7832  * Originally Released Under LGPL - original licence link has changed is not relivant.
7833  *
7834  * Fork - LGPL
7835  * <script type="text/javascript">
7836  */
7837
7838 /**
7839  * @class Roo.grid.AbstractSelectionModel
7840  * @extends Roo.util.Observable
7841  * @abstract
7842  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7843  * implemented by descendant classes.  This class should not be directly instantiated.
7844  * @constructor
7845  */
7846 Roo.grid.AbstractSelectionModel = function(){
7847     this.locked = false;
7848     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7849 };
7850
7851 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7852     /** @ignore Called by the grid automatically. Do not call directly. */
7853     init : function(grid){
7854         this.grid = grid;
7855         this.initEvents();
7856     },
7857
7858     /**
7859      * Locks the selections.
7860      */
7861     lock : function(){
7862         this.locked = true;
7863     },
7864
7865     /**
7866      * Unlocks the selections.
7867      */
7868     unlock : function(){
7869         this.locked = false;
7870     },
7871
7872     /**
7873      * Returns true if the selections are locked.
7874      * @return {Boolean}
7875      */
7876     isLocked : function(){
7877         return this.locked;
7878     }
7879 });/*
7880  * Based on:
7881  * Ext JS Library 1.1.1
7882  * Copyright(c) 2006-2007, Ext JS, LLC.
7883  *
7884  * Originally Released Under LGPL - original licence link has changed is not relivant.
7885  *
7886  * Fork - LGPL
7887  * <script type="text/javascript">
7888  */
7889 /**
7890  * @extends Roo.grid.AbstractSelectionModel
7891  * @class Roo.grid.RowSelectionModel
7892  * The default SelectionModel used by {@link Roo.grid.Grid}.
7893  * It supports multiple selections and keyboard selection/navigation. 
7894  * @constructor
7895  * @param {Object} config
7896  */
7897 Roo.grid.RowSelectionModel = function(config){
7898     Roo.apply(this, config);
7899     this.selections = new Roo.util.MixedCollection(false, function(o){
7900         return o.id;
7901     });
7902
7903     this.last = false;
7904     this.lastActive = false;
7905
7906     this.addEvents({
7907         /**
7908         * @event selectionchange
7909         * Fires when the selection changes
7910         * @param {SelectionModel} this
7911         */
7912        "selectionchange" : true,
7913        /**
7914         * @event afterselectionchange
7915         * Fires after the selection changes (eg. by key press or clicking)
7916         * @param {SelectionModel} this
7917         */
7918        "afterselectionchange" : true,
7919        /**
7920         * @event beforerowselect
7921         * Fires when a row is selected being selected, return false to cancel.
7922         * @param {SelectionModel} this
7923         * @param {Number} rowIndex The selected index
7924         * @param {Boolean} keepExisting False if other selections will be cleared
7925         */
7926        "beforerowselect" : true,
7927        /**
7928         * @event rowselect
7929         * Fires when a row is selected.
7930         * @param {SelectionModel} this
7931         * @param {Number} rowIndex The selected index
7932         * @param {Roo.data.Record} r The record
7933         */
7934        "rowselect" : true,
7935        /**
7936         * @event rowdeselect
7937         * Fires when a row is deselected.
7938         * @param {SelectionModel} this
7939         * @param {Number} rowIndex The selected index
7940         */
7941         "rowdeselect" : true
7942     });
7943     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7944     this.locked = false;
7945 };
7946
7947 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7948     /**
7949      * @cfg {Boolean} singleSelect
7950      * True to allow selection of only one row at a time (defaults to false)
7951      */
7952     singleSelect : false,
7953
7954     // private
7955     initEvents : function(){
7956
7957         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7958             this.grid.on("mousedown", this.handleMouseDown, this);
7959         }else{ // allow click to work like normal
7960             this.grid.on("rowclick", this.handleDragableRowClick, this);
7961         }
7962         // bootstrap does not have a view..
7963         var view = this.grid.view ? this.grid.view : this.grid;
7964         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7965             "up" : function(e){
7966                 if(!e.shiftKey){
7967                     this.selectPrevious(e.shiftKey);
7968                 }else if(this.last !== false && this.lastActive !== false){
7969                     var last = this.last;
7970                     this.selectRange(this.last,  this.lastActive-1);
7971                     view.focusRow(this.lastActive);
7972                     if(last !== false){
7973                         this.last = last;
7974                     }
7975                 }else{
7976                     this.selectFirstRow();
7977                 }
7978                 this.fireEvent("afterselectionchange", this);
7979             },
7980             "down" : function(e){
7981                 if(!e.shiftKey){
7982                     this.selectNext(e.shiftKey);
7983                 }else if(this.last !== false && this.lastActive !== false){
7984                     var last = this.last;
7985                     this.selectRange(this.last,  this.lastActive+1);
7986                     view.focusRow(this.lastActive);
7987                     if(last !== false){
7988                         this.last = last;
7989                     }
7990                 }else{
7991                     this.selectFirstRow();
7992                 }
7993                 this.fireEvent("afterselectionchange", this);
7994             },
7995             scope: this
7996         });
7997
7998          
7999         view.on("refresh", this.onRefresh, this);
8000         view.on("rowupdated", this.onRowUpdated, this);
8001         view.on("rowremoved", this.onRemove, this);
8002     },
8003
8004     // private
8005     onRefresh : function(){
8006         var ds = this.grid.ds, i, v = this.grid.view;
8007         var s = this.selections;
8008         s.each(function(r){
8009             if((i = ds.indexOfId(r.id)) != -1){
8010                 v.onRowSelect(i);
8011                 s.add(ds.getAt(i)); // updating the selection relate data
8012             }else{
8013                 s.remove(r);
8014             }
8015         });
8016     },
8017
8018     // private
8019     onRemove : function(v, index, r){
8020         this.selections.remove(r);
8021     },
8022
8023     // private
8024     onRowUpdated : function(v, index, r){
8025         if(this.isSelected(r)){
8026             v.onRowSelect(index);
8027         }
8028     },
8029
8030     /**
8031      * Select records.
8032      * @param {Array} records The records to select
8033      * @param {Boolean} keepExisting (optional) True to keep existing selections
8034      */
8035     selectRecords : function(records, keepExisting){
8036         if(!keepExisting){
8037             this.clearSelections();
8038         }
8039         var ds = this.grid.ds;
8040         for(var i = 0, len = records.length; i < len; i++){
8041             this.selectRow(ds.indexOf(records[i]), true);
8042         }
8043     },
8044
8045     /**
8046      * Gets the number of selected rows.
8047      * @return {Number}
8048      */
8049     getCount : function(){
8050         return this.selections.length;
8051     },
8052
8053     /**
8054      * Selects the first row in the grid.
8055      */
8056     selectFirstRow : function(){
8057         this.selectRow(0);
8058     },
8059
8060     /**
8061      * Select the last row.
8062      * @param {Boolean} keepExisting (optional) True to keep existing selections
8063      */
8064     selectLastRow : function(keepExisting){
8065         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8066     },
8067
8068     /**
8069      * Selects the row immediately following the last selected row.
8070      * @param {Boolean} keepExisting (optional) True to keep existing selections
8071      */
8072     selectNext : function(keepExisting){
8073         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8074             this.selectRow(this.last+1, keepExisting);
8075             var view = this.grid.view ? this.grid.view : this.grid;
8076             view.focusRow(this.last);
8077         }
8078     },
8079
8080     /**
8081      * Selects the row that precedes the last selected row.
8082      * @param {Boolean} keepExisting (optional) True to keep existing selections
8083      */
8084     selectPrevious : function(keepExisting){
8085         if(this.last){
8086             this.selectRow(this.last-1, keepExisting);
8087             var view = this.grid.view ? this.grid.view : this.grid;
8088             view.focusRow(this.last);
8089         }
8090     },
8091
8092     /**
8093      * Returns the selected records
8094      * @return {Array} Array of selected records
8095      */
8096     getSelections : function(){
8097         return [].concat(this.selections.items);
8098     },
8099
8100     /**
8101      * Returns the first selected record.
8102      * @return {Record}
8103      */
8104     getSelected : function(){
8105         return this.selections.itemAt(0);
8106     },
8107
8108
8109     /**
8110      * Clears all selections.
8111      */
8112     clearSelections : function(fast){
8113         if(this.locked) {
8114             return;
8115         }
8116         if(fast !== true){
8117             var ds = this.grid.ds;
8118             var s = this.selections;
8119             s.each(function(r){
8120                 this.deselectRow(ds.indexOfId(r.id));
8121             }, this);
8122             s.clear();
8123         }else{
8124             this.selections.clear();
8125         }
8126         this.last = false;
8127     },
8128
8129
8130     /**
8131      * Selects all rows.
8132      */
8133     selectAll : function(){
8134         if(this.locked) {
8135             return;
8136         }
8137         this.selections.clear();
8138         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8139             this.selectRow(i, true);
8140         }
8141     },
8142
8143     /**
8144      * Returns True if there is a selection.
8145      * @return {Boolean}
8146      */
8147     hasSelection : function(){
8148         return this.selections.length > 0;
8149     },
8150
8151     /**
8152      * Returns True if the specified row is selected.
8153      * @param {Number/Record} record The record or index of the record to check
8154      * @return {Boolean}
8155      */
8156     isSelected : function(index){
8157         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8158         return (r && this.selections.key(r.id) ? true : false);
8159     },
8160
8161     /**
8162      * Returns True if the specified record id is selected.
8163      * @param {String} id The id of record to check
8164      * @return {Boolean}
8165      */
8166     isIdSelected : function(id){
8167         return (this.selections.key(id) ? true : false);
8168     },
8169
8170     // private
8171     handleMouseDown : function(e, t)
8172     {
8173         var view = this.grid.view ? this.grid.view : this.grid;
8174         var rowIndex;
8175         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8176             return;
8177         };
8178         if(e.shiftKey && this.last !== false){
8179             var last = this.last;
8180             this.selectRange(last, rowIndex, e.ctrlKey);
8181             this.last = last; // reset the last
8182             view.focusRow(rowIndex);
8183         }else{
8184             var isSelected = this.isSelected(rowIndex);
8185             if(e.button !== 0 && isSelected){
8186                 view.focusRow(rowIndex);
8187             }else if(e.ctrlKey && isSelected){
8188                 this.deselectRow(rowIndex);
8189             }else if(!isSelected){
8190                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8191                 view.focusRow(rowIndex);
8192             }
8193         }
8194         this.fireEvent("afterselectionchange", this);
8195     },
8196     // private
8197     handleDragableRowClick :  function(grid, rowIndex, e) 
8198     {
8199         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8200             this.selectRow(rowIndex, false);
8201             var view = this.grid.view ? this.grid.view : this.grid;
8202             view.focusRow(rowIndex);
8203              this.fireEvent("afterselectionchange", this);
8204         }
8205     },
8206     
8207     /**
8208      * Selects multiple rows.
8209      * @param {Array} rows Array of the indexes of the row to select
8210      * @param {Boolean} keepExisting (optional) True to keep existing selections
8211      */
8212     selectRows : function(rows, keepExisting){
8213         if(!keepExisting){
8214             this.clearSelections();
8215         }
8216         for(var i = 0, len = rows.length; i < len; i++){
8217             this.selectRow(rows[i], true);
8218         }
8219     },
8220
8221     /**
8222      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8223      * @param {Number} startRow The index of the first row in the range
8224      * @param {Number} endRow The index of the last row in the range
8225      * @param {Boolean} keepExisting (optional) True to retain existing selections
8226      */
8227     selectRange : function(startRow, endRow, keepExisting){
8228         if(this.locked) {
8229             return;
8230         }
8231         if(!keepExisting){
8232             this.clearSelections();
8233         }
8234         if(startRow <= endRow){
8235             for(var i = startRow; i <= endRow; i++){
8236                 this.selectRow(i, true);
8237             }
8238         }else{
8239             for(var i = startRow; i >= endRow; i--){
8240                 this.selectRow(i, true);
8241             }
8242         }
8243     },
8244
8245     /**
8246      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8247      * @param {Number} startRow The index of the first row in the range
8248      * @param {Number} endRow The index of the last row in the range
8249      */
8250     deselectRange : function(startRow, endRow, preventViewNotify){
8251         if(this.locked) {
8252             return;
8253         }
8254         for(var i = startRow; i <= endRow; i++){
8255             this.deselectRow(i, preventViewNotify);
8256         }
8257     },
8258
8259     /**
8260      * Selects a row.
8261      * @param {Number} row The index of the row to select
8262      * @param {Boolean} keepExisting (optional) True to keep existing selections
8263      */
8264     selectRow : function(index, keepExisting, preventViewNotify){
8265         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8266             return;
8267         }
8268         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8269             if(!keepExisting || this.singleSelect){
8270                 this.clearSelections();
8271             }
8272             var r = this.grid.ds.getAt(index);
8273             this.selections.add(r);
8274             this.last = this.lastActive = index;
8275             if(!preventViewNotify){
8276                 var view = this.grid.view ? this.grid.view : this.grid;
8277                 view.onRowSelect(index);
8278             }
8279             this.fireEvent("rowselect", this, index, r);
8280             this.fireEvent("selectionchange", this);
8281         }
8282     },
8283
8284     /**
8285      * Deselects a row.
8286      * @param {Number} row The index of the row to deselect
8287      */
8288     deselectRow : function(index, preventViewNotify){
8289         if(this.locked) {
8290             return;
8291         }
8292         if(this.last == index){
8293             this.last = false;
8294         }
8295         if(this.lastActive == index){
8296             this.lastActive = false;
8297         }
8298         var r = this.grid.ds.getAt(index);
8299         this.selections.remove(r);
8300         if(!preventViewNotify){
8301             var view = this.grid.view ? this.grid.view : this.grid;
8302             view.onRowDeselect(index);
8303         }
8304         this.fireEvent("rowdeselect", this, index);
8305         this.fireEvent("selectionchange", this);
8306     },
8307
8308     // private
8309     restoreLast : function(){
8310         if(this._last){
8311             this.last = this._last;
8312         }
8313     },
8314
8315     // private
8316     acceptsNav : function(row, col, cm){
8317         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8318     },
8319
8320     // private
8321     onEditorKey : function(field, e){
8322         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8323         if(k == e.TAB){
8324             e.stopEvent();
8325             ed.completeEdit();
8326             if(e.shiftKey){
8327                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8328             }else{
8329                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8330             }
8331         }else if(k == e.ENTER && !e.ctrlKey){
8332             e.stopEvent();
8333             ed.completeEdit();
8334             if(e.shiftKey){
8335                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8336             }else{
8337                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8338             }
8339         }else if(k == e.ESC){
8340             ed.cancelEdit();
8341         }
8342         if(newCell){
8343             g.startEditing(newCell[0], newCell[1]);
8344         }
8345     }
8346 });/*
8347  * Based on:
8348  * Ext JS Library 1.1.1
8349  * Copyright(c) 2006-2007, Ext JS, LLC.
8350  *
8351  * Originally Released Under LGPL - original licence link has changed is not relivant.
8352  *
8353  * Fork - LGPL
8354  * <script type="text/javascript">
8355  */
8356  
8357
8358 /**
8359  * @class Roo.grid.ColumnModel
8360  * @extends Roo.util.Observable
8361  * This is the default implementation of a ColumnModel used by the Grid. It defines
8362  * the columns in the grid.
8363  * <br>Usage:<br>
8364  <pre><code>
8365  var colModel = new Roo.grid.ColumnModel([
8366         {header: "Ticker", width: 60, sortable: true, locked: true},
8367         {header: "Company Name", width: 150, sortable: true},
8368         {header: "Market Cap.", width: 100, sortable: true},
8369         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8370         {header: "Employees", width: 100, sortable: true, resizable: false}
8371  ]);
8372  </code></pre>
8373  * <p>
8374  
8375  * The config options listed for this class are options which may appear in each
8376  * individual column definition.
8377  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8378  * @constructor
8379  * @param {Object} config An Array of column config objects. See this class's
8380  * config objects for details.
8381 */
8382 Roo.grid.ColumnModel = function(config){
8383         /**
8384      * The config passed into the constructor
8385      */
8386     this.config = []; //config;
8387     this.lookup = {};
8388
8389     // if no id, create one
8390     // if the column does not have a dataIndex mapping,
8391     // map it to the order it is in the config
8392     for(var i = 0, len = config.length; i < len; i++){
8393         this.addColumn(config[i]);
8394         
8395     }
8396
8397     /**
8398      * The width of columns which have no width specified (defaults to 100)
8399      * @type Number
8400      */
8401     this.defaultWidth = 100;
8402
8403     /**
8404      * Default sortable of columns which have no sortable specified (defaults to false)
8405      * @type Boolean
8406      */
8407     this.defaultSortable = false;
8408
8409     this.addEvents({
8410         /**
8411              * @event widthchange
8412              * Fires when the width of a column changes.
8413              * @param {ColumnModel} this
8414              * @param {Number} columnIndex The column index
8415              * @param {Number} newWidth The new width
8416              */
8417             "widthchange": true,
8418         /**
8419              * @event headerchange
8420              * Fires when the text of a header changes.
8421              * @param {ColumnModel} this
8422              * @param {Number} columnIndex The column index
8423              * @param {Number} newText The new header text
8424              */
8425             "headerchange": true,
8426         /**
8427              * @event hiddenchange
8428              * Fires when a column is hidden or "unhidden".
8429              * @param {ColumnModel} this
8430              * @param {Number} columnIndex The column index
8431              * @param {Boolean} hidden true if hidden, false otherwise
8432              */
8433             "hiddenchange": true,
8434             /**
8435          * @event columnmoved
8436          * Fires when a column is moved.
8437          * @param {ColumnModel} this
8438          * @param {Number} oldIndex
8439          * @param {Number} newIndex
8440          */
8441         "columnmoved" : true,
8442         /**
8443          * @event columlockchange
8444          * Fires when a column's locked state is changed
8445          * @param {ColumnModel} this
8446          * @param {Number} colIndex
8447          * @param {Boolean} locked true if locked
8448          */
8449         "columnlockchange" : true
8450     });
8451     Roo.grid.ColumnModel.superclass.constructor.call(this);
8452 };
8453 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8454     /**
8455      * @cfg {String} header The header text to display in the Grid view.
8456      */
8457         /**
8458      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8459      */
8460         /**
8461      * @cfg {String} smHeader Header at Bootsrap Small width
8462      */
8463         /**
8464      * @cfg {String} mdHeader Header at Bootsrap Medium width
8465      */
8466         /**
8467      * @cfg {String} lgHeader Header at Bootsrap Large width
8468      */
8469         /**
8470      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8471      */
8472     /**
8473      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8474      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8475      * specified, the column's index is used as an index into the Record's data Array.
8476      */
8477     /**
8478      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8479      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8480      */
8481     /**
8482      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8483      * Defaults to the value of the {@link #defaultSortable} property.
8484      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8485      */
8486     /**
8487      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8488      */
8489     /**
8490      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8491      */
8492     /**
8493      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8494      */
8495     /**
8496      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8497      */
8498     /**
8499      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8500      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8501      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8502      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8503      */
8504        /**
8505      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8506      */
8507     /**
8508      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8509      */
8510     /**
8511      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8512      */
8513     /**
8514      * @cfg {String} cursor (Optional)
8515      */
8516     /**
8517      * @cfg {String} tooltip (Optional)
8518      */
8519     /**
8520      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8521      */
8522     /**
8523      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8524      */
8525     /**
8526      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8527      */
8528     /**
8529      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8530      */
8531         /**
8532      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8533      */
8534     /**
8535      * Returns the id of the column at the specified index.
8536      * @param {Number} index The column index
8537      * @return {String} the id
8538      */
8539     getColumnId : function(index){
8540         return this.config[index].id;
8541     },
8542
8543     /**
8544      * Returns the column for a specified id.
8545      * @param {String} id The column id
8546      * @return {Object} the column
8547      */
8548     getColumnById : function(id){
8549         return this.lookup[id];
8550     },
8551
8552     
8553     /**
8554      * Returns the column Object for a specified dataIndex.
8555      * @param {String} dataIndex The column dataIndex
8556      * @return {Object|Boolean} the column or false if not found
8557      */
8558     getColumnByDataIndex: function(dataIndex){
8559         var index = this.findColumnIndex(dataIndex);
8560         return index > -1 ? this.config[index] : false;
8561     },
8562     
8563     /**
8564      * Returns the index for a specified column id.
8565      * @param {String} id The column id
8566      * @return {Number} the index, or -1 if not found
8567      */
8568     getIndexById : function(id){
8569         for(var i = 0, len = this.config.length; i < len; i++){
8570             if(this.config[i].id == id){
8571                 return i;
8572             }
8573         }
8574         return -1;
8575     },
8576     
8577     /**
8578      * Returns the index for a specified column dataIndex.
8579      * @param {String} dataIndex The column dataIndex
8580      * @return {Number} the index, or -1 if not found
8581      */
8582     
8583     findColumnIndex : function(dataIndex){
8584         for(var i = 0, len = this.config.length; i < len; i++){
8585             if(this.config[i].dataIndex == dataIndex){
8586                 return i;
8587             }
8588         }
8589         return -1;
8590     },
8591     
8592     
8593     moveColumn : function(oldIndex, newIndex){
8594         var c = this.config[oldIndex];
8595         this.config.splice(oldIndex, 1);
8596         this.config.splice(newIndex, 0, c);
8597         this.dataMap = null;
8598         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8599     },
8600
8601     isLocked : function(colIndex){
8602         return this.config[colIndex].locked === true;
8603     },
8604
8605     setLocked : function(colIndex, value, suppressEvent){
8606         if(this.isLocked(colIndex) == value){
8607             return;
8608         }
8609         this.config[colIndex].locked = value;
8610         if(!suppressEvent){
8611             this.fireEvent("columnlockchange", this, colIndex, value);
8612         }
8613     },
8614
8615     getTotalLockedWidth : function(){
8616         var totalWidth = 0;
8617         for(var i = 0; i < this.config.length; i++){
8618             if(this.isLocked(i) && !this.isHidden(i)){
8619                 this.totalWidth += this.getColumnWidth(i);
8620             }
8621         }
8622         return totalWidth;
8623     },
8624
8625     getLockedCount : function(){
8626         for(var i = 0, len = this.config.length; i < len; i++){
8627             if(!this.isLocked(i)){
8628                 return i;
8629             }
8630         }
8631         
8632         return this.config.length;
8633     },
8634
8635     /**
8636      * Returns the number of columns.
8637      * @return {Number}
8638      */
8639     getColumnCount : function(visibleOnly){
8640         if(visibleOnly === true){
8641             var c = 0;
8642             for(var i = 0, len = this.config.length; i < len; i++){
8643                 if(!this.isHidden(i)){
8644                     c++;
8645                 }
8646             }
8647             return c;
8648         }
8649         return this.config.length;
8650     },
8651
8652     /**
8653      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8654      * @param {Function} fn
8655      * @param {Object} scope (optional)
8656      * @return {Array} result
8657      */
8658     getColumnsBy : function(fn, scope){
8659         var r = [];
8660         for(var i = 0, len = this.config.length; i < len; i++){
8661             var c = this.config[i];
8662             if(fn.call(scope||this, c, i) === true){
8663                 r[r.length] = c;
8664             }
8665         }
8666         return r;
8667     },
8668
8669     /**
8670      * Returns true if the specified column is sortable.
8671      * @param {Number} col The column index
8672      * @return {Boolean}
8673      */
8674     isSortable : function(col){
8675         if(typeof this.config[col].sortable == "undefined"){
8676             return this.defaultSortable;
8677         }
8678         return this.config[col].sortable;
8679     },
8680
8681     /**
8682      * Returns the rendering (formatting) function defined for the column.
8683      * @param {Number} col The column index.
8684      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8685      */
8686     getRenderer : function(col){
8687         if(!this.config[col].renderer){
8688             return Roo.grid.ColumnModel.defaultRenderer;
8689         }
8690         return this.config[col].renderer;
8691     },
8692
8693     /**
8694      * Sets the rendering (formatting) function for a column.
8695      * @param {Number} col The column index
8696      * @param {Function} fn The function to use to process the cell's raw data
8697      * to return HTML markup for the grid view. The render function is called with
8698      * the following parameters:<ul>
8699      * <li>Data value.</li>
8700      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8701      * <li>css A CSS style string to apply to the table cell.</li>
8702      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8703      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8704      * <li>Row index</li>
8705      * <li>Column index</li>
8706      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8707      */
8708     setRenderer : function(col, fn){
8709         this.config[col].renderer = fn;
8710     },
8711
8712     /**
8713      * Returns the width for the specified column.
8714      * @param {Number} col The column index
8715      * @param (optional) {String} gridSize bootstrap width size.
8716      * @return {Number}
8717      */
8718     getColumnWidth : function(col, gridSize)
8719         {
8720                 var cfg = this.config[col];
8721                 
8722                 if (typeof(gridSize) == 'undefined') {
8723                         return cfg.width * 1 || this.defaultWidth;
8724                 }
8725                 if (gridSize === false) { // if we set it..
8726                         return cfg.width || false;
8727                 }
8728                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8729                 
8730                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8731                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8732                                 continue;
8733                         }
8734                         return cfg[ sizes[i] ];
8735                 }
8736                 return 1;
8737                 
8738     },
8739
8740     /**
8741      * Sets the width for a column.
8742      * @param {Number} col The column index
8743      * @param {Number} width The new width
8744      */
8745     setColumnWidth : function(col, width, suppressEvent){
8746         this.config[col].width = width;
8747         this.totalWidth = null;
8748         if(!suppressEvent){
8749              this.fireEvent("widthchange", this, col, width);
8750         }
8751     },
8752
8753     /**
8754      * Returns the total width of all columns.
8755      * @param {Boolean} includeHidden True to include hidden column widths
8756      * @return {Number}
8757      */
8758     getTotalWidth : function(includeHidden){
8759         if(!this.totalWidth){
8760             this.totalWidth = 0;
8761             for(var i = 0, len = this.config.length; i < len; i++){
8762                 if(includeHidden || !this.isHidden(i)){
8763                     this.totalWidth += this.getColumnWidth(i);
8764                 }
8765             }
8766         }
8767         return this.totalWidth;
8768     },
8769
8770     /**
8771      * Returns the header for the specified column.
8772      * @param {Number} col The column index
8773      * @return {String}
8774      */
8775     getColumnHeader : function(col){
8776         return this.config[col].header;
8777     },
8778
8779     /**
8780      * Sets the header for a column.
8781      * @param {Number} col The column index
8782      * @param {String} header The new header
8783      */
8784     setColumnHeader : function(col, header){
8785         this.config[col].header = header;
8786         this.fireEvent("headerchange", this, col, header);
8787     },
8788
8789     /**
8790      * Returns the tooltip for the specified column.
8791      * @param {Number} col The column index
8792      * @return {String}
8793      */
8794     getColumnTooltip : function(col){
8795             return this.config[col].tooltip;
8796     },
8797     /**
8798      * Sets the tooltip for a column.
8799      * @param {Number} col The column index
8800      * @param {String} tooltip The new tooltip
8801      */
8802     setColumnTooltip : function(col, tooltip){
8803             this.config[col].tooltip = tooltip;
8804     },
8805
8806     /**
8807      * Returns the dataIndex for the specified column.
8808      * @param {Number} col The column index
8809      * @return {Number}
8810      */
8811     getDataIndex : function(col){
8812         return this.config[col].dataIndex;
8813     },
8814
8815     /**
8816      * Sets the dataIndex for a column.
8817      * @param {Number} col The column index
8818      * @param {Number} dataIndex The new dataIndex
8819      */
8820     setDataIndex : function(col, dataIndex){
8821         this.config[col].dataIndex = dataIndex;
8822     },
8823
8824     
8825     
8826     /**
8827      * Returns true if the cell is editable.
8828      * @param {Number} colIndex The column index
8829      * @param {Number} rowIndex The row index - this is nto actually used..?
8830      * @return {Boolean}
8831      */
8832     isCellEditable : function(colIndex, rowIndex){
8833         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8834     },
8835
8836     /**
8837      * Returns the editor defined for the cell/column.
8838      * return false or null to disable editing.
8839      * @param {Number} colIndex The column index
8840      * @param {Number} rowIndex The row index
8841      * @return {Object}
8842      */
8843     getCellEditor : function(colIndex, rowIndex){
8844         return this.config[colIndex].editor;
8845     },
8846
8847     /**
8848      * Sets if a column is editable.
8849      * @param {Number} col The column index
8850      * @param {Boolean} editable True if the column is editable
8851      */
8852     setEditable : function(col, editable){
8853         this.config[col].editable = editable;
8854     },
8855
8856
8857     /**
8858      * Returns true if the column is hidden.
8859      * @param {Number} colIndex The column index
8860      * @return {Boolean}
8861      */
8862     isHidden : function(colIndex){
8863         return this.config[colIndex].hidden;
8864     },
8865
8866
8867     /**
8868      * Returns true if the column width cannot be changed
8869      */
8870     isFixed : function(colIndex){
8871         return this.config[colIndex].fixed;
8872     },
8873
8874     /**
8875      * Returns true if the column can be resized
8876      * @return {Boolean}
8877      */
8878     isResizable : function(colIndex){
8879         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8880     },
8881     /**
8882      * Sets if a column is hidden.
8883      * @param {Number} colIndex The column index
8884      * @param {Boolean} hidden True if the column is hidden
8885      */
8886     setHidden : function(colIndex, hidden){
8887         this.config[colIndex].hidden = hidden;
8888         this.totalWidth = null;
8889         this.fireEvent("hiddenchange", this, colIndex, hidden);
8890     },
8891
8892     /**
8893      * Sets the editor for a column.
8894      * @param {Number} col The column index
8895      * @param {Object} editor The editor object
8896      */
8897     setEditor : function(col, editor){
8898         this.config[col].editor = editor;
8899     },
8900     /**
8901      * Add a column (experimental...) - defaults to adding to the end..
8902      * @param {Object} config 
8903     */
8904     addColumn : function(c)
8905     {
8906     
8907         var i = this.config.length;
8908         this.config[i] = c;
8909         
8910         if(typeof c.dataIndex == "undefined"){
8911             c.dataIndex = i;
8912         }
8913         if(typeof c.renderer == "string"){
8914             c.renderer = Roo.util.Format[c.renderer];
8915         }
8916         if(typeof c.id == "undefined"){
8917             c.id = Roo.id();
8918         }
8919         if(c.editor && c.editor.xtype){
8920             c.editor  = Roo.factory(c.editor, Roo.grid);
8921         }
8922         if(c.editor && c.editor.isFormField){
8923             c.editor = new Roo.grid.GridEditor(c.editor);
8924         }
8925         this.lookup[c.id] = c;
8926     }
8927     
8928 });
8929
8930 Roo.grid.ColumnModel.defaultRenderer = function(value)
8931 {
8932     if(typeof value == "object") {
8933         return value;
8934     }
8935         if(typeof value == "string" && value.length < 1){
8936             return "&#160;";
8937         }
8938     
8939         return String.format("{0}", value);
8940 };
8941
8942 // Alias for backwards compatibility
8943 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8944 /*
8945  * Based on:
8946  * Ext JS Library 1.1.1
8947  * Copyright(c) 2006-2007, Ext JS, LLC.
8948  *
8949  * Originally Released Under LGPL - original licence link has changed is not relivant.
8950  *
8951  * Fork - LGPL
8952  * <script type="text/javascript">
8953  */
8954  
8955 /**
8956  * @class Roo.LoadMask
8957  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8958  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8959  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8960  * element's UpdateManager load indicator and will be destroyed after the initial load.
8961  * @constructor
8962  * Create a new LoadMask
8963  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8964  * @param {Object} config The config object
8965  */
8966 Roo.LoadMask = function(el, config){
8967     this.el = Roo.get(el);
8968     Roo.apply(this, config);
8969     if(this.store){
8970         this.store.on('beforeload', this.onBeforeLoad, this);
8971         this.store.on('load', this.onLoad, this);
8972         this.store.on('loadexception', this.onLoadException, this);
8973         this.removeMask = false;
8974     }else{
8975         var um = this.el.getUpdateManager();
8976         um.showLoadIndicator = false; // disable the default indicator
8977         um.on('beforeupdate', this.onBeforeLoad, this);
8978         um.on('update', this.onLoad, this);
8979         um.on('failure', this.onLoad, this);
8980         this.removeMask = true;
8981     }
8982 };
8983
8984 Roo.LoadMask.prototype = {
8985     /**
8986      * @cfg {Boolean} removeMask
8987      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8988      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8989      */
8990     removeMask : false,
8991     /**
8992      * @cfg {String} msg
8993      * The text to display in a centered loading message box (defaults to 'Loading...')
8994      */
8995     msg : 'Loading...',
8996     /**
8997      * @cfg {String} msgCls
8998      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8999      */
9000     msgCls : 'x-mask-loading',
9001
9002     /**
9003      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9004      * @type Boolean
9005      */
9006     disabled: false,
9007
9008     /**
9009      * Disables the mask to prevent it from being displayed
9010      */
9011     disable : function(){
9012        this.disabled = true;
9013     },
9014
9015     /**
9016      * Enables the mask so that it can be displayed
9017      */
9018     enable : function(){
9019         this.disabled = false;
9020     },
9021     
9022     onLoadException : function()
9023     {
9024         Roo.log(arguments);
9025         
9026         if (typeof(arguments[3]) != 'undefined') {
9027             Roo.MessageBox.alert("Error loading",arguments[3]);
9028         } 
9029         /*
9030         try {
9031             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9032                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9033             }   
9034         } catch(e) {
9035             
9036         }
9037         */
9038     
9039         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9040     },
9041     // private
9042     onLoad : function()
9043     {
9044         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9045     },
9046
9047     // private
9048     onBeforeLoad : function(){
9049         if(!this.disabled){
9050             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9051         }
9052     },
9053
9054     // private
9055     destroy : function(){
9056         if(this.store){
9057             this.store.un('beforeload', this.onBeforeLoad, this);
9058             this.store.un('load', this.onLoad, this);
9059             this.store.un('loadexception', this.onLoadException, this);
9060         }else{
9061             var um = this.el.getUpdateManager();
9062             um.un('beforeupdate', this.onBeforeLoad, this);
9063             um.un('update', this.onLoad, this);
9064             um.un('failure', this.onLoad, this);
9065         }
9066     }
9067 };/**
9068  * @class Roo.bootstrap.Table
9069  * @licence LGBL
9070  * @extends Roo.bootstrap.Component
9071  * @children Roo.bootstrap.TableBody
9072  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9073  * Similar to Roo.grid.Grid
9074  * <pre><code>
9075  var table = Roo.factory({
9076     xtype : 'Table',
9077     xns : Roo.bootstrap,
9078     autoSizeColumns: true,
9079     
9080     
9081     store : {
9082         xtype : 'Store',
9083         xns : Roo.data,
9084         remoteSort : true,
9085         sortInfo : { direction : 'ASC', field: 'name' },
9086         proxy : {
9087            xtype : 'HttpProxy',
9088            xns : Roo.data,
9089            method : 'GET',
9090            url : 'https://example.com/some.data.url.json'
9091         },
9092         reader : {
9093            xtype : 'JsonReader',
9094            xns : Roo.data,
9095            fields : [ 'id', 'name', whatever' ],
9096            id : 'id',
9097            root : 'data'
9098         }
9099     },
9100     cm : [
9101         {
9102             xtype : 'ColumnModel',
9103             xns : Roo.grid,
9104             align : 'center',
9105             cursor : 'pointer',
9106             dataIndex : 'is_in_group',
9107             header : "Name",
9108             sortable : true,
9109             renderer : function(v, x , r) {  
9110             
9111                 return String.format("{0}", v)
9112             }
9113             width : 3
9114         } // more columns..
9115     ],
9116     selModel : {
9117         xtype : 'RowSelectionModel',
9118         xns : Roo.bootstrap.Table
9119         // you can add listeners to catch selection change here....
9120     }
9121      
9122
9123  });
9124  // set any options
9125  grid.render(Roo.get("some-div"));
9126 </code></pre>
9127
9128 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9129
9130
9131
9132  *
9133  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9134  * @cfg {Roo.data.Store} store The data store to use
9135  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9136  * 
9137  * @cfg {String} cls table class
9138  *
9139  *
9140  * @cfg {string} empty_results  Text to display for no results 
9141  * @cfg {boolean} striped Should the rows be alternative striped
9142  * @cfg {boolean} bordered Add borders to the table
9143  * @cfg {boolean} hover Add hover highlighting
9144  * @cfg {boolean} condensed Format condensed
9145  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9146  *                also adds table-responsive (see bootstrap docs for details)
9147  * @cfg {Boolean} loadMask (true|false) default false
9148  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9149  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9150  * @cfg {Boolean} rowSelection (true|false) default false
9151  * @cfg {Boolean} cellSelection (true|false) default false
9152  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9153  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9154  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9155  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9156  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9157  *
9158  * 
9159  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9160  * 
9161  * @constructor
9162  * Create a new Table
9163  * @param {Object} config The config object
9164  */
9165
9166 Roo.bootstrap.Table = function(config)
9167 {
9168     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9169      
9170     // BC...
9171     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9172     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9173     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9174     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9175     
9176     this.view = this; // compat with grid.
9177     
9178     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9179     if (this.sm) {
9180         this.sm.grid = this;
9181         this.selModel = Roo.factory(this.sm, Roo.grid);
9182         this.sm = this.selModel;
9183         this.sm.xmodule = this.xmodule || false;
9184     }
9185     
9186     if (this.cm && typeof(this.cm.config) == 'undefined') {
9187         this.colModel = new Roo.grid.ColumnModel(this.cm);
9188         this.cm = this.colModel;
9189         this.cm.xmodule = this.xmodule || false;
9190     }
9191     if (this.store) {
9192         this.store= Roo.factory(this.store, Roo.data);
9193         this.ds = this.store;
9194         this.ds.xmodule = this.xmodule || false;
9195          
9196     }
9197     if (this.footer && this.store) {
9198         this.footer.dataSource = this.ds;
9199         this.footer = Roo.factory(this.footer);
9200     }
9201     
9202     /** @private */
9203     this.addEvents({
9204         /**
9205          * @event cellclick
9206          * Fires when a cell is clicked
9207          * @param {Roo.bootstrap.Table} this
9208          * @param {Roo.Element} el
9209          * @param {Number} rowIndex
9210          * @param {Number} columnIndex
9211          * @param {Roo.EventObject} e
9212          */
9213         "cellclick" : true,
9214         /**
9215          * @event celldblclick
9216          * Fires when a cell is double clicked
9217          * @param {Roo.bootstrap.Table} this
9218          * @param {Roo.Element} el
9219          * @param {Number} rowIndex
9220          * @param {Number} columnIndex
9221          * @param {Roo.EventObject} e
9222          */
9223         "celldblclick" : true,
9224         /**
9225          * @event rowclick
9226          * Fires when a row is clicked
9227          * @param {Roo.bootstrap.Table} this
9228          * @param {Roo.Element} el
9229          * @param {Number} rowIndex
9230          * @param {Roo.EventObject} e
9231          */
9232         "rowclick" : true,
9233         /**
9234          * @event rowdblclick
9235          * Fires when a row is double clicked
9236          * @param {Roo.bootstrap.Table} this
9237          * @param {Roo.Element} el
9238          * @param {Number} rowIndex
9239          * @param {Roo.EventObject} e
9240          */
9241         "rowdblclick" : true,
9242         /**
9243          * @event mouseover
9244          * Fires when a mouseover occur
9245          * @param {Roo.bootstrap.Table} this
9246          * @param {Roo.Element} el
9247          * @param {Number} rowIndex
9248          * @param {Number} columnIndex
9249          * @param {Roo.EventObject} e
9250          */
9251         "mouseover" : true,
9252         /**
9253          * @event mouseout
9254          * Fires when a mouseout occur
9255          * @param {Roo.bootstrap.Table} this
9256          * @param {Roo.Element} el
9257          * @param {Number} rowIndex
9258          * @param {Number} columnIndex
9259          * @param {Roo.EventObject} e
9260          */
9261         "mouseout" : true,
9262         /**
9263          * @event rowclass
9264          * Fires when a row is rendered, so you can change add a style to it.
9265          * @param {Roo.bootstrap.Table} this
9266          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9267          */
9268         'rowclass' : true,
9269           /**
9270          * @event rowsrendered
9271          * Fires when all the  rows have been rendered
9272          * @param {Roo.bootstrap.Table} this
9273          */
9274         'rowsrendered' : true,
9275         /**
9276          * @event contextmenu
9277          * The raw contextmenu event for the entire grid.
9278          * @param {Roo.EventObject} e
9279          */
9280         "contextmenu" : true,
9281         /**
9282          * @event rowcontextmenu
9283          * Fires when a row is right clicked
9284          * @param {Roo.bootstrap.Table} this
9285          * @param {Number} rowIndex
9286          * @param {Roo.EventObject} e
9287          */
9288         "rowcontextmenu" : true,
9289         /**
9290          * @event cellcontextmenu
9291          * Fires when a cell is right clicked
9292          * @param {Roo.bootstrap.Table} this
9293          * @param {Number} rowIndex
9294          * @param {Number} cellIndex
9295          * @param {Roo.EventObject} e
9296          */
9297          "cellcontextmenu" : true,
9298          /**
9299          * @event headercontextmenu
9300          * Fires when a header is right clicked
9301          * @param {Roo.bootstrap.Table} this
9302          * @param {Number} columnIndex
9303          * @param {Roo.EventObject} e
9304          */
9305         "headercontextmenu" : true,
9306         /**
9307          * @event mousedown
9308          * The raw mousedown event for the entire grid.
9309          * @param {Roo.EventObject} e
9310          */
9311         "mousedown" : true
9312         
9313     });
9314 };
9315
9316 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9317     
9318     cls: false,
9319     
9320     empty_results : '',
9321     striped : false,
9322     scrollBody : false,
9323     bordered: false,
9324     hover:  false,
9325     condensed : false,
9326     responsive : false,
9327     sm : false,
9328     cm : false,
9329     store : false,
9330     loadMask : false,
9331     footerShow : true,
9332     headerShow : true,
9333     enableColumnResize: true,
9334   
9335     rowSelection : false,
9336     cellSelection : false,
9337     layout : false,
9338
9339     minColumnWidth : 50,
9340     
9341     // Roo.Element - the tbody
9342     bodyEl: false,  // <tbody> Roo.Element - thead element    
9343     headEl: false,  // <thead> Roo.Element - thead element
9344     resizeProxy : false, // proxy element for dragging?
9345
9346
9347     
9348     container: false, // used by gridpanel...
9349     
9350     lazyLoad : false,
9351     
9352     CSS : Roo.util.CSS,
9353     
9354     auto_hide_footer : false,
9355     
9356     view: false, // actually points to this..
9357     
9358     getAutoCreate : function()
9359     {
9360         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9361         
9362         cfg = {
9363             tag: 'table',
9364             cls : 'table', 
9365             cn : []
9366         };
9367         // this get's auto added by panel.Grid
9368         if (this.scrollBody) {
9369             cfg.cls += ' table-body-fixed';
9370         }    
9371         if (this.striped) {
9372             cfg.cls += ' table-striped';
9373         }
9374         
9375         if (this.hover) {
9376             cfg.cls += ' table-hover';
9377         }
9378         if (this.bordered) {
9379             cfg.cls += ' table-bordered';
9380         }
9381         if (this.condensed) {
9382             cfg.cls += ' table-condensed';
9383         }
9384         
9385         if (this.responsive) {
9386             cfg.cls += ' table-responsive';
9387         }
9388         
9389         if (this.cls) {
9390             cfg.cls+=  ' ' +this.cls;
9391         }
9392         
9393         
9394         
9395         if (this.layout) {
9396             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9397         }
9398         
9399         if(this.store || this.cm){
9400             if(this.headerShow){
9401                 cfg.cn.push(this.renderHeader());
9402             }
9403             
9404             cfg.cn.push(this.renderBody());
9405             
9406             if(this.footerShow){
9407                 cfg.cn.push(this.renderFooter());
9408             }
9409             // where does this come from?
9410             //cfg.cls+=  ' TableGrid';
9411         }
9412         
9413         return { cn : [ cfg ] };
9414     },
9415     
9416     initEvents : function()
9417     {   
9418         if(!this.store || !this.cm){
9419             return;
9420         }
9421         if (this.selModel) {
9422             this.selModel.initEvents();
9423         }
9424         
9425         
9426         //Roo.log('initEvents with ds!!!!');
9427         
9428         this.bodyEl = this.el.select('tbody', true).first();
9429         this.headEl = this.el.select('thead', true).first();
9430         this.mainFoot = this.el.select('tfoot', true).first();
9431         
9432         
9433         
9434         
9435         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9436             e.on('click', this.sort, this);
9437         }, this);
9438         
9439         
9440         // why is this done????? = it breaks dialogs??
9441         //this.parent().el.setStyle('position', 'relative');
9442         
9443         
9444         if (this.footer) {
9445             this.footer.parentId = this.id;
9446             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9447             
9448             if(this.lazyLoad){
9449                 this.el.select('tfoot tr td').first().addClass('hide');
9450             }
9451         } 
9452         
9453         if(this.loadMask) {
9454             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9455         }
9456         
9457         this.store.on('load', this.onLoad, this);
9458         this.store.on('beforeload', this.onBeforeLoad, this);
9459         this.store.on('update', this.onUpdate, this);
9460         this.store.on('add', this.onAdd, this);
9461         this.store.on("clear", this.clear, this);
9462         
9463         this.el.on("contextmenu", this.onContextMenu, this);
9464         
9465         
9466         this.cm.on("headerchange", this.onHeaderChange, this);
9467         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9468
9469  //?? does bodyEl get replaced on render?
9470         this.bodyEl.on("click", this.onClick, this);
9471         this.bodyEl.on("dblclick", this.onDblClick, this);        
9472         this.bodyEl.on('scroll', this.onBodyScroll, this);
9473
9474         // guessing mainbody will work - this relays usually caught by selmodel at present.
9475         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9476   
9477   
9478         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9479         
9480   
9481         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9482             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9483         }
9484         
9485         this.initCSS();
9486     },
9487     // Compatibility with grid - we implement all the view features at present.
9488     getView : function()
9489     {
9490         return this;
9491     },
9492     
9493     initCSS : function()
9494     {
9495         
9496         
9497         var cm = this.cm, styles = [];
9498         this.CSS.removeStyleSheet(this.id + '-cssrules');
9499         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9500         // we can honour xs/sm/md/xl  as widths...
9501         // we first have to decide what widht we are currently at...
9502         var sz = Roo.getGridSize();
9503         
9504         var total = 0;
9505         var last = -1;
9506         var cols = []; // visable cols.
9507         var total_abs = 0;
9508         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9509             var w = cm.getColumnWidth(i, false);
9510             if(cm.isHidden(i)){
9511                 cols.push( { rel : false, abs : 0 });
9512                 continue;
9513             }
9514             if (w !== false) {
9515                 cols.push( { rel : false, abs : w });
9516                 total_abs += w;
9517                 last = i; // not really..
9518                 continue;
9519             }
9520             var w = cm.getColumnWidth(i, sz);
9521             if (w > 0) {
9522                 last = i
9523             }
9524             total += w;
9525             cols.push( { rel : w, abs : false });
9526         }
9527         
9528         var avail = this.bodyEl.dom.clientWidth - total_abs;
9529         
9530         var unitWidth = Math.floor(avail / total);
9531         var rem = avail - (unitWidth * total);
9532         
9533         var hidden, width, pos = 0 , splithide , left;
9534         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9535             
9536             hidden = 'display:none;';
9537             left = '';
9538             width  = 'width:0px;';
9539             splithide = '';
9540             if(!cm.isHidden(i)){
9541                 hidden = '';
9542                 
9543                 
9544                 // we can honour xs/sm/md/xl ?
9545                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9546                 if (w===0) {
9547                     hidden = 'display:none;';
9548                 }
9549                 // width should return a small number...
9550                 if (i == last) {
9551                     w+=rem; // add the remaining with..
9552                 }
9553                 pos += w;
9554                 left = "left:" + (pos -4) + "px;";
9555                 width = "width:" + w+ "px;";
9556                 
9557             }
9558             if (this.responsive) {
9559                 width = '';
9560                 left = '';
9561                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9562                 splithide = 'display: none;';
9563             }
9564             
9565             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9566             if (this.headEl) {
9567                 if (i == last) {
9568                     splithide = 'display:none;';
9569                 }
9570                 
9571                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9572                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9573                             // this is the popover version..
9574                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9575                 );
9576             }
9577             
9578         }
9579         //Roo.log(styles.join(''));
9580         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9581         
9582     },
9583     
9584     
9585     
9586     onContextMenu : function(e, t)
9587     {
9588         this.processEvent("contextmenu", e);
9589     },
9590     
9591     processEvent : function(name, e)
9592     {
9593         if (name != 'touchstart' ) {
9594             this.fireEvent(name, e);    
9595         }
9596         
9597         var t = e.getTarget();
9598         
9599         var cell = Roo.get(t);
9600         
9601         if(!cell){
9602             return;
9603         }
9604         
9605         if(cell.findParent('tfoot', false, true)){
9606             return;
9607         }
9608         
9609         if(cell.findParent('thead', false, true)){
9610             
9611             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9612                 cell = Roo.get(t).findParent('th', false, true);
9613                 if (!cell) {
9614                     Roo.log("failed to find th in thead?");
9615                     Roo.log(e.getTarget());
9616                     return;
9617                 }
9618             }
9619             
9620             var cellIndex = cell.dom.cellIndex;
9621             
9622             var ename = name == 'touchstart' ? 'click' : name;
9623             this.fireEvent("header" + ename, this, cellIndex, e);
9624             
9625             return;
9626         }
9627         
9628         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9629             cell = Roo.get(t).findParent('td', false, true);
9630             if (!cell) {
9631                 Roo.log("failed to find th in tbody?");
9632                 Roo.log(e.getTarget());
9633                 return;
9634             }
9635         }
9636         
9637         var row = cell.findParent('tr', false, true);
9638         var cellIndex = cell.dom.cellIndex;
9639         var rowIndex = row.dom.rowIndex - 1;
9640         
9641         if(row !== false){
9642             
9643             this.fireEvent("row" + name, this, rowIndex, e);
9644             
9645             if(cell !== false){
9646             
9647                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9648             }
9649         }
9650         
9651     },
9652     
9653     onMouseover : function(e, el)
9654     {
9655         var cell = Roo.get(el);
9656         
9657         if(!cell){
9658             return;
9659         }
9660         
9661         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9662             cell = cell.findParent('td', false, true);
9663         }
9664         
9665         var row = cell.findParent('tr', false, true);
9666         var cellIndex = cell.dom.cellIndex;
9667         var rowIndex = row.dom.rowIndex - 1; // start from 0
9668         
9669         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9670         
9671     },
9672     
9673     onMouseout : function(e, el)
9674     {
9675         var cell = Roo.get(el);
9676         
9677         if(!cell){
9678             return;
9679         }
9680         
9681         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9682             cell = cell.findParent('td', false, true);
9683         }
9684         
9685         var row = cell.findParent('tr', false, true);
9686         var cellIndex = cell.dom.cellIndex;
9687         var rowIndex = row.dom.rowIndex - 1; // start from 0
9688         
9689         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9690         
9691     },
9692     
9693     onClick : function(e, el)
9694     {
9695         var cell = Roo.get(el);
9696         
9697         if(!cell || (!this.cellSelection && !this.rowSelection)){
9698             return;
9699         }
9700         
9701         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9702             cell = cell.findParent('td', false, true);
9703         }
9704         
9705         if(!cell || typeof(cell) == 'undefined'){
9706             return;
9707         }
9708         
9709         var row = cell.findParent('tr', false, true);
9710         
9711         if(!row || typeof(row) == 'undefined'){
9712             return;
9713         }
9714         
9715         var cellIndex = cell.dom.cellIndex;
9716         var rowIndex = this.getRowIndex(row);
9717         
9718         // why??? - should these not be based on SelectionModel?
9719         //if(this.cellSelection){
9720             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9721         //}
9722         
9723         //if(this.rowSelection){
9724             this.fireEvent('rowclick', this, row, rowIndex, e);
9725         //}
9726          
9727     },
9728         
9729     onDblClick : function(e,el)
9730     {
9731         var cell = Roo.get(el);
9732         
9733         if(!cell || (!this.cellSelection && !this.rowSelection)){
9734             return;
9735         }
9736         
9737         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9738             cell = cell.findParent('td', false, true);
9739         }
9740         
9741         if(!cell || typeof(cell) == 'undefined'){
9742             return;
9743         }
9744         
9745         var row = cell.findParent('tr', false, true);
9746         
9747         if(!row || typeof(row) == 'undefined'){
9748             return;
9749         }
9750         
9751         var cellIndex = cell.dom.cellIndex;
9752         var rowIndex = this.getRowIndex(row);
9753         
9754         if(this.cellSelection){
9755             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9756         }
9757         
9758         if(this.rowSelection){
9759             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9760         }
9761     },
9762     findRowIndex : function(el)
9763     {
9764         var cell = Roo.get(el);
9765         if(!cell) {
9766             return false;
9767         }
9768         var row = cell.findParent('tr', false, true);
9769         
9770         if(!row || typeof(row) == 'undefined'){
9771             return false;
9772         }
9773         return this.getRowIndex(row);
9774     },
9775     sort : function(e,el)
9776     {
9777         var col = Roo.get(el);
9778         
9779         if(!col.hasClass('sortable')){
9780             return;
9781         }
9782         
9783         var sort = col.attr('sort');
9784         var dir = 'ASC';
9785         
9786         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9787             dir = 'DESC';
9788         }
9789         
9790         this.store.sortInfo = {field : sort, direction : dir};
9791         
9792         if (this.footer) {
9793             Roo.log("calling footer first");
9794             this.footer.onClick('first');
9795         } else {
9796         
9797             this.store.load({ params : { start : 0 } });
9798         }
9799     },
9800     
9801     renderHeader : function()
9802     {
9803         var header = {
9804             tag: 'thead',
9805             cn : []
9806         };
9807         
9808         var cm = this.cm;
9809         this.totalWidth = 0;
9810         
9811         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9812             
9813             var config = cm.config[i];
9814             
9815             var c = {
9816                 tag: 'th',
9817                 cls : 'x-hcol-' + i,
9818                 style : '',
9819                 
9820                 html: cm.getColumnHeader(i)
9821             };
9822             
9823             var tooltip = cm.getColumnTooltip(i);
9824             if (tooltip) {
9825                 c.tooltip = tooltip;
9826             }
9827             
9828             
9829             var hh = '';
9830             
9831             if(typeof(config.sortable) != 'undefined' && config.sortable){
9832                 c.cls += ' sortable';
9833                 c.html = '<i class="fa"></i>' + c.html;
9834             }
9835             
9836             // could use BS4 hidden-..-down 
9837             
9838             if(typeof(config.lgHeader) != 'undefined'){
9839                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9840             }
9841             
9842             if(typeof(config.mdHeader) != 'undefined'){
9843                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9844             }
9845             
9846             if(typeof(config.smHeader) != 'undefined'){
9847                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9848             }
9849             
9850             if(typeof(config.xsHeader) != 'undefined'){
9851                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9852             }
9853             
9854             if(hh.length){
9855                 c.html = hh;
9856             }
9857             
9858             if(typeof(config.tooltip) != 'undefined'){
9859                 c.tooltip = config.tooltip;
9860             }
9861             
9862             if(typeof(config.colspan) != 'undefined'){
9863                 c.colspan = config.colspan;
9864             }
9865             
9866             // hidden is handled by CSS now
9867             
9868             if(typeof(config.dataIndex) != 'undefined'){
9869                 c.sort = config.dataIndex;
9870             }
9871             
9872            
9873             
9874             if(typeof(config.align) != 'undefined' && config.align.length){
9875                 c.style += ' text-align:' + config.align + ';';
9876             }
9877             
9878             /* width is done in CSS
9879              *if(typeof(config.width) != 'undefined'){
9880                 c.style += ' width:' + config.width + 'px;';
9881                 this.totalWidth += config.width;
9882             } else {
9883                 this.totalWidth += 100; // assume minimum of 100 per column?
9884             }
9885             */
9886             
9887             if(typeof(config.cls) != 'undefined'){
9888                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9889             }
9890             // this is the bit that doesnt reall work at all...
9891             
9892             if (this.responsive) {
9893                  
9894             
9895                 ['xs','sm','md','lg'].map(function(size){
9896                     
9897                     if(typeof(config[size]) == 'undefined'){
9898                         return;
9899                     }
9900                      
9901                     if (!config[size]) { // 0 = hidden
9902                         // BS 4 '0' is treated as hide that column and below.
9903                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9904                         return;
9905                     }
9906                     
9907                     c.cls += ' col-' + size + '-' + config[size] + (
9908                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9909                     );
9910                     
9911                     
9912                 });
9913             }
9914             // at the end?
9915             
9916             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9917             
9918             
9919             
9920             
9921             header.cn.push(c)
9922         }
9923         
9924         return header;
9925     },
9926     
9927     renderBody : function()
9928     {
9929         var body = {
9930             tag: 'tbody',
9931             cn : [
9932                 {
9933                     tag: 'tr',
9934                     cn : [
9935                         {
9936                             tag : 'td',
9937                             colspan :  this.cm.getColumnCount()
9938                         }
9939                     ]
9940                 }
9941             ]
9942         };
9943         
9944         return body;
9945     },
9946     
9947     renderFooter : function()
9948     {
9949         var footer = {
9950             tag: 'tfoot',
9951             cn : [
9952                 {
9953                     tag: 'tr',
9954                     cn : [
9955                         {
9956                             tag : 'td',
9957                             colspan :  this.cm.getColumnCount()
9958                         }
9959                     ]
9960                 }
9961             ]
9962         };
9963         
9964         return footer;
9965     },
9966     
9967     
9968     
9969     onLoad : function()
9970     {
9971 //        Roo.log('ds onload');
9972         this.clear();
9973         
9974         var _this = this;
9975         var cm = this.cm;
9976         var ds = this.store;
9977         
9978         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9979             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9980             if (_this.store.sortInfo) {
9981                     
9982                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9983                     e.select('i', true).addClass(['fa-arrow-up']);
9984                 }
9985                 
9986                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9987                     e.select('i', true).addClass(['fa-arrow-down']);
9988                 }
9989             }
9990         });
9991         
9992         var tbody =  this.bodyEl;
9993               
9994         if(ds.getCount() > 0){
9995             ds.data.each(function(d,rowIndex){
9996                 var row =  this.renderRow(cm, ds, rowIndex);
9997                 
9998                 tbody.createChild(row);
9999                 
10000                 var _this = this;
10001                 
10002                 if(row.cellObjects.length){
10003                     Roo.each(row.cellObjects, function(r){
10004                         _this.renderCellObject(r);
10005                     })
10006                 }
10007                 
10008             }, this);
10009         } else if (this.empty_results.length) {
10010             this.el.mask(this.empty_results, 'no-spinner');
10011         }
10012         
10013         var tfoot = this.el.select('tfoot', true).first();
10014         
10015         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10016             
10017             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10018             
10019             var total = this.ds.getTotalCount();
10020             
10021             if(this.footer.pageSize < total){
10022                 this.mainFoot.show();
10023             }
10024         }
10025         
10026         Roo.each(this.el.select('tbody td', true).elements, function(e){
10027             e.on('mouseover', _this.onMouseover, _this);
10028         });
10029         
10030         Roo.each(this.el.select('tbody td', true).elements, function(e){
10031             e.on('mouseout', _this.onMouseout, _this);
10032         });
10033         this.fireEvent('rowsrendered', this);
10034         
10035         this.autoSize();
10036         
10037         this.initCSS(); /// resize cols
10038
10039         
10040     },
10041     
10042     
10043     onUpdate : function(ds,record)
10044     {
10045         this.refreshRow(record);
10046         this.autoSize();
10047     },
10048     
10049     onRemove : function(ds, record, index, isUpdate){
10050         if(isUpdate !== true){
10051             this.fireEvent("beforerowremoved", this, index, record);
10052         }
10053         var bt = this.bodyEl.dom;
10054         
10055         var rows = this.el.select('tbody > tr', true).elements;
10056         
10057         if(typeof(rows[index]) != 'undefined'){
10058             bt.removeChild(rows[index].dom);
10059         }
10060         
10061 //        if(bt.rows[index]){
10062 //            bt.removeChild(bt.rows[index]);
10063 //        }
10064         
10065         if(isUpdate !== true){
10066             //this.stripeRows(index);
10067             //this.syncRowHeights(index, index);
10068             //this.layout();
10069             this.fireEvent("rowremoved", this, index, record);
10070         }
10071     },
10072     
10073     onAdd : function(ds, records, rowIndex)
10074     {
10075         //Roo.log('on Add called');
10076         // - note this does not handle multiple adding very well..
10077         var bt = this.bodyEl.dom;
10078         for (var i =0 ; i < records.length;i++) {
10079             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10080             //Roo.log(records[i]);
10081             //Roo.log(this.store.getAt(rowIndex+i));
10082             this.insertRow(this.store, rowIndex + i, false);
10083             return;
10084         }
10085         
10086     },
10087     
10088     
10089     refreshRow : function(record){
10090         var ds = this.store, index;
10091         if(typeof record == 'number'){
10092             index = record;
10093             record = ds.getAt(index);
10094         }else{
10095             index = ds.indexOf(record);
10096             if (index < 0) {
10097                 return; // should not happen - but seems to 
10098             }
10099         }
10100         this.insertRow(ds, index, true);
10101         this.autoSize();
10102         this.onRemove(ds, record, index+1, true);
10103         this.autoSize();
10104         //this.syncRowHeights(index, index);
10105         //this.layout();
10106         this.fireEvent("rowupdated", this, index, record);
10107     },
10108     // private - called by RowSelection
10109     onRowSelect : function(rowIndex){
10110         var row = this.getRowDom(rowIndex);
10111         row.addClass(['bg-info','info']);
10112     },
10113     // private - called by RowSelection
10114     onRowDeselect : function(rowIndex)
10115     {
10116         if (rowIndex < 0) {
10117             return;
10118         }
10119         var row = this.getRowDom(rowIndex);
10120         row.removeClass(['bg-info','info']);
10121     },
10122       /**
10123      * Focuses the specified row.
10124      * @param {Number} row The row index
10125      */
10126     focusRow : function(row)
10127     {
10128         //Roo.log('GridView.focusRow');
10129         var x = this.bodyEl.dom.scrollLeft;
10130         this.focusCell(row, 0, false);
10131         this.bodyEl.dom.scrollLeft = x;
10132
10133     },
10134      /**
10135      * Focuses the specified cell.
10136      * @param {Number} row The row index
10137      * @param {Number} col The column index
10138      * @param {Boolean} hscroll false to disable horizontal scrolling
10139      */
10140     focusCell : function(row, col, hscroll)
10141     {
10142         //Roo.log('GridView.focusCell');
10143         var el = this.ensureVisible(row, col, hscroll);
10144         // not sure what focusEL achives = it's a <a> pos relative 
10145         //this.focusEl.alignTo(el, "tl-tl");
10146         //if(Roo.isGecko){
10147         //    this.focusEl.focus();
10148         //}else{
10149         //    this.focusEl.focus.defer(1, this.focusEl);
10150         //}
10151     },
10152     
10153      /**
10154      * Scrolls the specified cell into view
10155      * @param {Number} row The row index
10156      * @param {Number} col The column index
10157      * @param {Boolean} hscroll false to disable horizontal scrolling
10158      */
10159     ensureVisible : function(row, col, hscroll)
10160     {
10161         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10162         //return null; //disable for testing.
10163         if(typeof row != "number"){
10164             row = row.rowIndex;
10165         }
10166         if(row < 0 && row >= this.ds.getCount()){
10167             return  null;
10168         }
10169         col = (col !== undefined ? col : 0);
10170         var cm = this.cm;
10171         while(cm.isHidden(col)){
10172             col++;
10173         }
10174
10175         var el = this.getCellDom(row, col);
10176         if(!el){
10177             return null;
10178         }
10179         var c = this.bodyEl.dom;
10180
10181         var ctop = parseInt(el.offsetTop, 10);
10182         var cleft = parseInt(el.offsetLeft, 10);
10183         var cbot = ctop + el.offsetHeight;
10184         var cright = cleft + el.offsetWidth;
10185
10186         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10187         var ch = 0; //?? header is not withing the area?
10188         var stop = parseInt(c.scrollTop, 10);
10189         var sleft = parseInt(c.scrollLeft, 10);
10190         var sbot = stop + ch;
10191         var sright = sleft + c.clientWidth;
10192         /*
10193         Roo.log('GridView.ensureVisible:' +
10194                 ' ctop:' + ctop +
10195                 ' c.clientHeight:' + c.clientHeight +
10196                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10197                 ' stop:' + stop +
10198                 ' cbot:' + cbot +
10199                 ' sbot:' + sbot +
10200                 ' ch:' + ch  
10201                 );
10202         */
10203         if(ctop < stop){
10204             c.scrollTop = ctop;
10205             //Roo.log("set scrolltop to ctop DISABLE?");
10206         }else if(cbot > sbot){
10207             //Roo.log("set scrolltop to cbot-ch");
10208             c.scrollTop = cbot-ch;
10209         }
10210
10211         if(hscroll !== false){
10212             if(cleft < sleft){
10213                 c.scrollLeft = cleft;
10214             }else if(cright > sright){
10215                 c.scrollLeft = cright-c.clientWidth;
10216             }
10217         }
10218
10219         return el;
10220     },
10221     
10222     
10223     insertRow : function(dm, rowIndex, isUpdate){
10224         
10225         if(!isUpdate){
10226             this.fireEvent("beforerowsinserted", this, rowIndex);
10227         }
10228             //var s = this.getScrollState();
10229         var row = this.renderRow(this.cm, this.store, rowIndex);
10230         // insert before rowIndex..
10231         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10232         
10233         var _this = this;
10234                 
10235         if(row.cellObjects.length){
10236             Roo.each(row.cellObjects, function(r){
10237                 _this.renderCellObject(r);
10238             })
10239         }
10240             
10241         if(!isUpdate){
10242             this.fireEvent("rowsinserted", this, rowIndex);
10243             //this.syncRowHeights(firstRow, lastRow);
10244             //this.stripeRows(firstRow);
10245             //this.layout();
10246         }
10247         
10248     },
10249     
10250     
10251     getRowDom : function(rowIndex)
10252     {
10253         var rows = this.el.select('tbody > tr', true).elements;
10254         
10255         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10256         
10257     },
10258     getCellDom : function(rowIndex, colIndex)
10259     {
10260         var row = this.getRowDom(rowIndex);
10261         if (row === false) {
10262             return false;
10263         }
10264         var cols = row.select('td', true).elements;
10265         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10266         
10267     },
10268     
10269     // returns the object tree for a tr..
10270   
10271     
10272     renderRow : function(cm, ds, rowIndex) 
10273     {
10274         var d = ds.getAt(rowIndex);
10275         
10276         var row = {
10277             tag : 'tr',
10278             cls : 'x-row-' + rowIndex,
10279             cn : []
10280         };
10281             
10282         var cellObjects = [];
10283         
10284         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10285             var config = cm.config[i];
10286             
10287             var renderer = cm.getRenderer(i);
10288             var value = '';
10289             var id = false;
10290             
10291             if(typeof(renderer) !== 'undefined'){
10292                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10293             }
10294             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10295             // and are rendered into the cells after the row is rendered - using the id for the element.
10296             
10297             if(typeof(value) === 'object'){
10298                 id = Roo.id();
10299                 cellObjects.push({
10300                     container : id,
10301                     cfg : value 
10302                 })
10303             }
10304             
10305             var rowcfg = {
10306                 record: d,
10307                 rowIndex : rowIndex,
10308                 colIndex : i,
10309                 rowClass : ''
10310             };
10311
10312             this.fireEvent('rowclass', this, rowcfg);
10313             
10314             var td = {
10315                 tag: 'td',
10316                 // this might end up displaying HTML?
10317                 // this is too messy... - better to only do it on columsn you know are going to be too long
10318                 //tooltip : (typeof(value) === 'object') ? '' : value,
10319                 cls : rowcfg.rowClass + ' x-col-' + i,
10320                 style: '',
10321                 html: (typeof(value) === 'object') ? '' : value
10322             };
10323             
10324             if (id) {
10325                 td.id = id;
10326             }
10327             
10328             if(typeof(config.colspan) != 'undefined'){
10329                 td.colspan = config.colspan;
10330             }
10331             
10332             
10333             
10334             if(typeof(config.align) != 'undefined' && config.align.length){
10335                 td.style += ' text-align:' + config.align + ';';
10336             }
10337             if(typeof(config.valign) != 'undefined' && config.valign.length){
10338                 td.style += ' vertical-align:' + config.valign + ';';
10339             }
10340             /*
10341             if(typeof(config.width) != 'undefined'){
10342                 td.style += ' width:' +  config.width + 'px;';
10343             }
10344             */
10345             
10346             if(typeof(config.cursor) != 'undefined'){
10347                 td.style += ' cursor:' +  config.cursor + ';';
10348             }
10349             
10350             if(typeof(config.cls) != 'undefined'){
10351                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10352             }
10353             if (this.responsive) {
10354                 ['xs','sm','md','lg'].map(function(size){
10355                     
10356                     if(typeof(config[size]) == 'undefined'){
10357                         return;
10358                     }
10359                     
10360                     
10361                       
10362                     if (!config[size]) { // 0 = hidden
10363                         // BS 4 '0' is treated as hide that column and below.
10364                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10365                         return;
10366                     }
10367                     
10368                     td.cls += ' col-' + size + '-' + config[size] + (
10369                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10370                     );
10371                      
10372     
10373                 });
10374             }
10375             row.cn.push(td);
10376            
10377         }
10378         
10379         row.cellObjects = cellObjects;
10380         
10381         return row;
10382           
10383     },
10384     
10385     
10386     
10387     onBeforeLoad : function()
10388     {
10389         this.el.unmask(); // if needed.
10390     },
10391      /**
10392      * Remove all rows
10393      */
10394     clear : function()
10395     {
10396         this.el.select('tbody', true).first().dom.innerHTML = '';
10397     },
10398     /**
10399      * Show or hide a row.
10400      * @param {Number} rowIndex to show or hide
10401      * @param {Boolean} state hide
10402      */
10403     setRowVisibility : function(rowIndex, state)
10404     {
10405         var bt = this.bodyEl.dom;
10406         
10407         var rows = this.el.select('tbody > tr', true).elements;
10408         
10409         if(typeof(rows[rowIndex]) == 'undefined'){
10410             return;
10411         }
10412         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10413         
10414     },
10415     
10416     
10417     getSelectionModel : function(){
10418         if(!this.selModel){
10419             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10420         }
10421         return this.selModel;
10422     },
10423     /*
10424      * Render the Roo.bootstrap object from renderder
10425      */
10426     renderCellObject : function(r)
10427     {
10428         var _this = this;
10429         
10430         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10431         
10432         var t = r.cfg.render(r.container);
10433         
10434         if(r.cfg.cn){
10435             Roo.each(r.cfg.cn, function(c){
10436                 var child = {
10437                     container: t.getChildContainer(),
10438                     cfg: c
10439                 };
10440                 _this.renderCellObject(child);
10441             })
10442         }
10443     },
10444     /**
10445      * get the Row Index from a dom element.
10446      * @param {Roo.Element} row The row to look for
10447      * @returns {Number} the row
10448      */
10449     getRowIndex : function(row)
10450     {
10451         var rowIndex = -1;
10452         
10453         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10454             if(el != row){
10455                 return;
10456             }
10457             
10458             rowIndex = index;
10459         });
10460         
10461         return rowIndex;
10462     },
10463     /**
10464      * get the header TH element for columnIndex
10465      * @param {Number} columnIndex
10466      * @returns {Roo.Element}
10467      */
10468     getHeaderIndex: function(colIndex)
10469     {
10470         var cols = this.headEl.select('th', true).elements;
10471         return cols[colIndex]; 
10472     },
10473     /**
10474      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10475      * @param {domElement} cell to look for
10476      * @returns {Number} the column
10477      */
10478     getCellIndex : function(cell)
10479     {
10480         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10481         if(id){
10482             return parseInt(id[1], 10);
10483         }
10484         return 0;
10485     },
10486      /**
10487      * Returns the grid's underlying element = used by panel.Grid
10488      * @return {Element} The element
10489      */
10490     getGridEl : function(){
10491         return this.el;
10492     },
10493      /**
10494      * Forces a resize - used by panel.Grid
10495      * @return {Element} The element
10496      */
10497     autoSize : function()
10498     {
10499         //var ctr = Roo.get(this.container.dom.parentElement);
10500         var ctr = Roo.get(this.el.dom);
10501         
10502         var thd = this.getGridEl().select('thead',true).first();
10503         var tbd = this.getGridEl().select('tbody', true).first();
10504         var tfd = this.getGridEl().select('tfoot', true).first();
10505         
10506         var cw = ctr.getWidth();
10507         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10508         
10509         if (tbd) {
10510             
10511             tbd.setWidth(ctr.getWidth());
10512             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10513             // this needs fixing for various usage - currently only hydra job advers I think..
10514             //tdb.setHeight(
10515             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10516             //); 
10517             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10518             cw -= barsize;
10519         }
10520         cw = Math.max(cw, this.totalWidth);
10521         this.getGridEl().select('tbody tr',true).setWidth(cw);
10522         this.initCSS();
10523         
10524         // resize 'expandable coloumn?
10525         
10526         return; // we doe not have a view in this design..
10527         
10528     },
10529     onBodyScroll: function()
10530     {
10531         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10532         if(this.headEl){
10533             this.headEl.setStyle({
10534                 'position' : 'relative',
10535                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10536             });
10537         }
10538         
10539         if(this.lazyLoad){
10540             
10541             var scrollHeight = this.bodyEl.dom.scrollHeight;
10542             
10543             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10544             
10545             var height = this.bodyEl.getHeight();
10546             
10547             if(scrollHeight - height == scrollTop) {
10548                 
10549                 var total = this.ds.getTotalCount();
10550                 
10551                 if(this.footer.cursor + this.footer.pageSize < total){
10552                     
10553                     this.footer.ds.load({
10554                         params : {
10555                             start : this.footer.cursor + this.footer.pageSize,
10556                             limit : this.footer.pageSize
10557                         },
10558                         add : true
10559                     });
10560                 }
10561             }
10562             
10563         }
10564     },
10565     onColumnSplitterMoved : function(i, diff)
10566     {
10567         this.userResized = true;
10568         
10569         var cm = this.colModel;
10570         
10571         var w = this.getHeaderIndex(i).getWidth() + diff;
10572         
10573         
10574         cm.setColumnWidth(i, w, true);
10575         this.initCSS();
10576         //var cid = cm.getColumnId(i); << not used in this version?
10577        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10578         
10579         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10580         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10581         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10582 */
10583         //this.updateSplitters();
10584         //this.layout(); << ??
10585         this.fireEvent("columnresize", i, w);
10586     },
10587     onHeaderChange : function()
10588     {
10589         var header = this.renderHeader();
10590         var table = this.el.select('table', true).first();
10591         
10592         this.headEl.remove();
10593         this.headEl = table.createChild(header, this.bodyEl, false);
10594         
10595         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10596             e.on('click', this.sort, this);
10597         }, this);
10598         
10599         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10600             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10601         }
10602         
10603     },
10604     
10605     onHiddenChange : function(colModel, colIndex, hidden)
10606     {
10607         /*
10608         this.cm.setHidden()
10609         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10610         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10611         
10612         this.CSS.updateRule(thSelector, "display", "");
10613         this.CSS.updateRule(tdSelector, "display", "");
10614         
10615         if(hidden){
10616             this.CSS.updateRule(thSelector, "display", "none");
10617             this.CSS.updateRule(tdSelector, "display", "none");
10618         }
10619         */
10620         // onload calls initCSS()
10621         this.onHeaderChange();
10622         this.onLoad();
10623     },
10624     
10625     setColumnWidth: function(col_index, width)
10626     {
10627         // width = "md-2 xs-2..."
10628         if(!this.colModel.config[col_index]) {
10629             return;
10630         }
10631         
10632         var w = width.split(" ");
10633         
10634         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10635         
10636         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10637         
10638         
10639         for(var j = 0; j < w.length; j++) {
10640             
10641             if(!w[j]) {
10642                 continue;
10643             }
10644             
10645             var size_cls = w[j].split("-");
10646             
10647             if(!Number.isInteger(size_cls[1] * 1)) {
10648                 continue;
10649             }
10650             
10651             if(!this.colModel.config[col_index][size_cls[0]]) {
10652                 continue;
10653             }
10654             
10655             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10656                 continue;
10657             }
10658             
10659             h_row[0].classList.replace(
10660                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10661                 "col-"+size_cls[0]+"-"+size_cls[1]
10662             );
10663             
10664             for(var i = 0; i < rows.length; i++) {
10665                 
10666                 var size_cls = w[j].split("-");
10667                 
10668                 if(!Number.isInteger(size_cls[1] * 1)) {
10669                     continue;
10670                 }
10671                 
10672                 if(!this.colModel.config[col_index][size_cls[0]]) {
10673                     continue;
10674                 }
10675                 
10676                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10677                     continue;
10678                 }
10679                 
10680                 rows[i].classList.replace(
10681                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10682                     "col-"+size_cls[0]+"-"+size_cls[1]
10683                 );
10684             }
10685             
10686             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10687         }
10688     }
10689 });
10690
10691 // currently only used to find the split on drag.. 
10692 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10693
10694 /**
10695  * @depricated
10696 */
10697 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10698 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10699 /*
10700  * - LGPL
10701  *
10702  * table cell
10703  * 
10704  */
10705
10706 /**
10707  * @class Roo.bootstrap.TableCell
10708  * @extends Roo.bootstrap.Component
10709  * @children Roo.bootstrap.Component
10710  * @parent Roo.bootstrap.TableRow
10711  * Bootstrap TableCell class
10712  * 
10713  * @cfg {String} html cell contain text
10714  * @cfg {String} cls cell class
10715  * @cfg {String} tag cell tag (td|th) default td
10716  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10717  * @cfg {String} align Aligns the content in a cell
10718  * @cfg {String} axis Categorizes cells
10719  * @cfg {String} bgcolor Specifies the background color of a cell
10720  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10721  * @cfg {Number} colspan Specifies the number of columns a cell should span
10722  * @cfg {String} headers Specifies one or more header cells a cell is related to
10723  * @cfg {Number} height Sets the height of a cell
10724  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10725  * @cfg {Number} rowspan Sets the number of rows a cell should span
10726  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10727  * @cfg {String} valign Vertical aligns the content in a cell
10728  * @cfg {Number} width Specifies the width of a cell
10729  * 
10730  * @constructor
10731  * Create a new TableCell
10732  * @param {Object} config The config object
10733  */
10734
10735 Roo.bootstrap.TableCell = function(config){
10736     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10737 };
10738
10739 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10740     
10741     html: false,
10742     cls: false,
10743     tag: false,
10744     abbr: false,
10745     align: false,
10746     axis: false,
10747     bgcolor: false,
10748     charoff: false,
10749     colspan: false,
10750     headers: false,
10751     height: false,
10752     nowrap: false,
10753     rowspan: false,
10754     scope: false,
10755     valign: false,
10756     width: false,
10757     
10758     
10759     getAutoCreate : function(){
10760         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10761         
10762         cfg = {
10763             tag: 'td'
10764         };
10765         
10766         if(this.tag){
10767             cfg.tag = this.tag;
10768         }
10769         
10770         if (this.html) {
10771             cfg.html=this.html
10772         }
10773         if (this.cls) {
10774             cfg.cls=this.cls
10775         }
10776         if (this.abbr) {
10777             cfg.abbr=this.abbr
10778         }
10779         if (this.align) {
10780             cfg.align=this.align
10781         }
10782         if (this.axis) {
10783             cfg.axis=this.axis
10784         }
10785         if (this.bgcolor) {
10786             cfg.bgcolor=this.bgcolor
10787         }
10788         if (this.charoff) {
10789             cfg.charoff=this.charoff
10790         }
10791         if (this.colspan) {
10792             cfg.colspan=this.colspan
10793         }
10794         if (this.headers) {
10795             cfg.headers=this.headers
10796         }
10797         if (this.height) {
10798             cfg.height=this.height
10799         }
10800         if (this.nowrap) {
10801             cfg.nowrap=this.nowrap
10802         }
10803         if (this.rowspan) {
10804             cfg.rowspan=this.rowspan
10805         }
10806         if (this.scope) {
10807             cfg.scope=this.scope
10808         }
10809         if (this.valign) {
10810             cfg.valign=this.valign
10811         }
10812         if (this.width) {
10813             cfg.width=this.width
10814         }
10815         
10816         
10817         return cfg;
10818     }
10819    
10820 });
10821
10822  
10823
10824  /*
10825  * - LGPL
10826  *
10827  * table row
10828  * 
10829  */
10830
10831 /**
10832  * @class Roo.bootstrap.TableRow
10833  * @extends Roo.bootstrap.Component
10834  * @children Roo.bootstrap.TableCell
10835  * @parent Roo.bootstrap.TableBody
10836  * Bootstrap TableRow class
10837  * @cfg {String} cls row class
10838  * @cfg {String} align Aligns the content in a table row
10839  * @cfg {String} bgcolor Specifies a background color for a table row
10840  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10841  * @cfg {String} valign Vertical aligns the content in a table row
10842  * 
10843  * @constructor
10844  * Create a new TableRow
10845  * @param {Object} config The config object
10846  */
10847
10848 Roo.bootstrap.TableRow = function(config){
10849     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10850 };
10851
10852 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10853     
10854     cls: false,
10855     align: false,
10856     bgcolor: false,
10857     charoff: false,
10858     valign: false,
10859     
10860     getAutoCreate : function(){
10861         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10862         
10863         cfg = {
10864             tag: 'tr'
10865         };
10866             
10867         if(this.cls){
10868             cfg.cls = this.cls;
10869         }
10870         if(this.align){
10871             cfg.align = this.align;
10872         }
10873         if(this.bgcolor){
10874             cfg.bgcolor = this.bgcolor;
10875         }
10876         if(this.charoff){
10877             cfg.charoff = this.charoff;
10878         }
10879         if(this.valign){
10880             cfg.valign = this.valign;
10881         }
10882         
10883         return cfg;
10884     }
10885    
10886 });
10887
10888  
10889
10890  /*
10891  * - LGPL
10892  *
10893  * table body
10894  * 
10895  */
10896
10897 /**
10898  * @class Roo.bootstrap.TableBody
10899  * @extends Roo.bootstrap.Component
10900  * @children Roo.bootstrap.TableRow
10901  * @parent Roo.bootstrap.Table
10902  * Bootstrap TableBody class
10903  * @cfg {String} cls element class
10904  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10905  * @cfg {String} align Aligns the content inside the element
10906  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10907  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10908  * 
10909  * @constructor
10910  * Create a new TableBody
10911  * @param {Object} config The config object
10912  */
10913
10914 Roo.bootstrap.TableBody = function(config){
10915     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10916 };
10917
10918 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10919     
10920     cls: false,
10921     tag: false,
10922     align: false,
10923     charoff: false,
10924     valign: false,
10925     
10926     getAutoCreate : function(){
10927         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10928         
10929         cfg = {
10930             tag: 'tbody'
10931         };
10932             
10933         if (this.cls) {
10934             cfg.cls=this.cls
10935         }
10936         if(this.tag){
10937             cfg.tag = this.tag;
10938         }
10939         
10940         if(this.align){
10941             cfg.align = this.align;
10942         }
10943         if(this.charoff){
10944             cfg.charoff = this.charoff;
10945         }
10946         if(this.valign){
10947             cfg.valign = this.valign;
10948         }
10949         
10950         return cfg;
10951     }
10952     
10953     
10954 //    initEvents : function()
10955 //    {
10956 //        
10957 //        if(!this.store){
10958 //            return;
10959 //        }
10960 //        
10961 //        this.store = Roo.factory(this.store, Roo.data);
10962 //        this.store.on('load', this.onLoad, this);
10963 //        
10964 //        this.store.load();
10965 //        
10966 //    },
10967 //    
10968 //    onLoad: function () 
10969 //    {   
10970 //        this.fireEvent('load', this);
10971 //    }
10972 //    
10973 //   
10974 });
10975
10976  
10977
10978  /*
10979  * Based on:
10980  * Ext JS Library 1.1.1
10981  * Copyright(c) 2006-2007, Ext JS, LLC.
10982  *
10983  * Originally Released Under LGPL - original licence link has changed is not relivant.
10984  *
10985  * Fork - LGPL
10986  * <script type="text/javascript">
10987  */
10988
10989 // as we use this in bootstrap.
10990 Roo.namespace('Roo.form');
10991  /**
10992  * @class Roo.form.Action
10993  * Internal Class used to handle form actions
10994  * @constructor
10995  * @param {Roo.form.BasicForm} el The form element or its id
10996  * @param {Object} config Configuration options
10997  */
10998
10999  
11000  
11001 // define the action interface
11002 Roo.form.Action = function(form, options){
11003     this.form = form;
11004     this.options = options || {};
11005 };
11006 /**
11007  * Client Validation Failed
11008  * @const 
11009  */
11010 Roo.form.Action.CLIENT_INVALID = 'client';
11011 /**
11012  * Server Validation Failed
11013  * @const 
11014  */
11015 Roo.form.Action.SERVER_INVALID = 'server';
11016  /**
11017  * Connect to Server Failed
11018  * @const 
11019  */
11020 Roo.form.Action.CONNECT_FAILURE = 'connect';
11021 /**
11022  * Reading Data from Server Failed
11023  * @const 
11024  */
11025 Roo.form.Action.LOAD_FAILURE = 'load';
11026
11027 Roo.form.Action.prototype = {
11028     type : 'default',
11029     failureType : undefined,
11030     response : undefined,
11031     result : undefined,
11032
11033     // interface method
11034     run : function(options){
11035
11036     },
11037
11038     // interface method
11039     success : function(response){
11040
11041     },
11042
11043     // interface method
11044     handleResponse : function(response){
11045
11046     },
11047
11048     // default connection failure
11049     failure : function(response){
11050         
11051         this.response = response;
11052         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11053         this.form.afterAction(this, false);
11054     },
11055
11056     processResponse : function(response){
11057         this.response = response;
11058         if(!response.responseText){
11059             return true;
11060         }
11061         this.result = this.handleResponse(response);
11062         return this.result;
11063     },
11064
11065     // utility functions used internally
11066     getUrl : function(appendParams){
11067         var url = this.options.url || this.form.url || this.form.el.dom.action;
11068         if(appendParams){
11069             var p = this.getParams();
11070             if(p){
11071                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11072             }
11073         }
11074         return url;
11075     },
11076
11077     getMethod : function(){
11078         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11079     },
11080
11081     getParams : function(){
11082         var bp = this.form.baseParams;
11083         var p = this.options.params;
11084         if(p){
11085             if(typeof p == "object"){
11086                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11087             }else if(typeof p == 'string' && bp){
11088                 p += '&' + Roo.urlEncode(bp);
11089             }
11090         }else if(bp){
11091             p = Roo.urlEncode(bp);
11092         }
11093         return p;
11094     },
11095
11096     createCallback : function(){
11097         return {
11098             success: this.success,
11099             failure: this.failure,
11100             scope: this,
11101             timeout: (this.form.timeout*1000),
11102             upload: this.form.fileUpload ? this.success : undefined
11103         };
11104     }
11105 };
11106
11107 Roo.form.Action.Submit = function(form, options){
11108     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11109 };
11110
11111 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11112     type : 'submit',
11113
11114     haveProgress : false,
11115     uploadComplete : false,
11116     
11117     // uploadProgress indicator.
11118     uploadProgress : function()
11119     {
11120         if (!this.form.progressUrl) {
11121             return;
11122         }
11123         
11124         if (!this.haveProgress) {
11125             Roo.MessageBox.progress("Uploading", "Uploading");
11126         }
11127         if (this.uploadComplete) {
11128            Roo.MessageBox.hide();
11129            return;
11130         }
11131         
11132         this.haveProgress = true;
11133    
11134         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11135         
11136         var c = new Roo.data.Connection();
11137         c.request({
11138             url : this.form.progressUrl,
11139             params: {
11140                 id : uid
11141             },
11142             method: 'GET',
11143             success : function(req){
11144                //console.log(data);
11145                 var rdata = false;
11146                 var edata;
11147                 try  {
11148                    rdata = Roo.decode(req.responseText)
11149                 } catch (e) {
11150                     Roo.log("Invalid data from server..");
11151                     Roo.log(edata);
11152                     return;
11153                 }
11154                 if (!rdata || !rdata.success) {
11155                     Roo.log(rdata);
11156                     Roo.MessageBox.alert(Roo.encode(rdata));
11157                     return;
11158                 }
11159                 var data = rdata.data;
11160                 
11161                 if (this.uploadComplete) {
11162                    Roo.MessageBox.hide();
11163                    return;
11164                 }
11165                    
11166                 if (data){
11167                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11168                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11169                     );
11170                 }
11171                 this.uploadProgress.defer(2000,this);
11172             },
11173        
11174             failure: function(data) {
11175                 Roo.log('progress url failed ');
11176                 Roo.log(data);
11177             },
11178             scope : this
11179         });
11180            
11181     },
11182     
11183     
11184     run : function()
11185     {
11186         // run get Values on the form, so it syncs any secondary forms.
11187         this.form.getValues();
11188         
11189         var o = this.options;
11190         var method = this.getMethod();
11191         var isPost = method == 'POST';
11192         if(o.clientValidation === false || this.form.isValid()){
11193             
11194             if (this.form.progressUrl) {
11195                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11196                     (new Date() * 1) + '' + Math.random());
11197                     
11198             } 
11199             
11200             
11201             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11202                 form:this.form.el.dom,
11203                 url:this.getUrl(!isPost),
11204                 method: method,
11205                 params:isPost ? this.getParams() : null,
11206                 isUpload: this.form.fileUpload,
11207                 formData : this.form.formData
11208             }));
11209             
11210             this.uploadProgress();
11211
11212         }else if (o.clientValidation !== false){ // client validation failed
11213             this.failureType = Roo.form.Action.CLIENT_INVALID;
11214             this.form.afterAction(this, false);
11215         }
11216     },
11217
11218     success : function(response)
11219     {
11220         this.uploadComplete= true;
11221         if (this.haveProgress) {
11222             Roo.MessageBox.hide();
11223         }
11224         
11225         
11226         var result = this.processResponse(response);
11227         if(result === true || result.success){
11228             this.form.afterAction(this, true);
11229             return;
11230         }
11231         if(result.errors){
11232             this.form.markInvalid(result.errors);
11233             this.failureType = Roo.form.Action.SERVER_INVALID;
11234         }
11235         this.form.afterAction(this, false);
11236     },
11237     failure : function(response)
11238     {
11239         this.uploadComplete= true;
11240         if (this.haveProgress) {
11241             Roo.MessageBox.hide();
11242         }
11243         
11244         this.response = response;
11245         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11246         this.form.afterAction(this, false);
11247     },
11248     
11249     handleResponse : function(response){
11250         if(this.form.errorReader){
11251             var rs = this.form.errorReader.read(response);
11252             var errors = [];
11253             if(rs.records){
11254                 for(var i = 0, len = rs.records.length; i < len; i++) {
11255                     var r = rs.records[i];
11256                     errors[i] = r.data;
11257                 }
11258             }
11259             if(errors.length < 1){
11260                 errors = null;
11261             }
11262             return {
11263                 success : rs.success,
11264                 errors : errors
11265             };
11266         }
11267         var ret = false;
11268         try {
11269             ret = Roo.decode(response.responseText);
11270         } catch (e) {
11271             ret = {
11272                 success: false,
11273                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11274                 errors : []
11275             };
11276         }
11277         return ret;
11278         
11279     }
11280 });
11281
11282
11283 Roo.form.Action.Load = function(form, options){
11284     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11285     this.reader = this.form.reader;
11286 };
11287
11288 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11289     type : 'load',
11290
11291     run : function(){
11292         
11293         Roo.Ajax.request(Roo.apply(
11294                 this.createCallback(), {
11295                     method:this.getMethod(),
11296                     url:this.getUrl(false),
11297                     params:this.getParams()
11298         }));
11299     },
11300
11301     success : function(response){
11302         
11303         var result = this.processResponse(response);
11304         if(result === true || !result.success || !result.data){
11305             this.failureType = Roo.form.Action.LOAD_FAILURE;
11306             this.form.afterAction(this, false);
11307             return;
11308         }
11309         this.form.clearInvalid();
11310         this.form.setValues(result.data);
11311         this.form.afterAction(this, true);
11312     },
11313
11314     handleResponse : function(response){
11315         if(this.form.reader){
11316             var rs = this.form.reader.read(response);
11317             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11318             return {
11319                 success : rs.success,
11320                 data : data
11321             };
11322         }
11323         return Roo.decode(response.responseText);
11324     }
11325 });
11326
11327 Roo.form.Action.ACTION_TYPES = {
11328     'load' : Roo.form.Action.Load,
11329     'submit' : Roo.form.Action.Submit
11330 };/*
11331  * - LGPL
11332  *
11333  * form
11334  *
11335  */
11336
11337 /**
11338  * @class Roo.bootstrap.form.Form
11339  * @extends Roo.bootstrap.Component
11340  * @children Roo.bootstrap.Component
11341  * Bootstrap Form class
11342  * @cfg {String} method  GET | POST (default POST)
11343  * @cfg {String} labelAlign top | left (default top)
11344  * @cfg {String} align left  | right - for navbars
11345  * @cfg {Boolean} loadMask load mask when submit (default true)
11346
11347  *
11348  * @constructor
11349  * Create a new Form
11350  * @param {Object} config The config object
11351  */
11352
11353
11354 Roo.bootstrap.form.Form = function(config){
11355     
11356     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11357     
11358     Roo.bootstrap.form.Form.popover.apply();
11359     
11360     this.addEvents({
11361         /**
11362          * @event clientvalidation
11363          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11364          * @param {Form} this
11365          * @param {Boolean} valid true if the form has passed client-side validation
11366          */
11367         clientvalidation: true,
11368         /**
11369          * @event beforeaction
11370          * Fires before any action is performed. Return false to cancel the action.
11371          * @param {Form} this
11372          * @param {Action} action The action to be performed
11373          */
11374         beforeaction: true,
11375         /**
11376          * @event actionfailed
11377          * Fires when an action fails.
11378          * @param {Form} this
11379          * @param {Action} action The action that failed
11380          */
11381         actionfailed : true,
11382         /**
11383          * @event actioncomplete
11384          * Fires when an action is completed.
11385          * @param {Form} this
11386          * @param {Action} action The action that completed
11387          */
11388         actioncomplete : true
11389     });
11390 };
11391
11392 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11393
11394      /**
11395      * @cfg {String} method
11396      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11397      */
11398     method : 'POST',
11399     /**
11400      * @cfg {String} url
11401      * The URL to use for form actions if one isn't supplied in the action options.
11402      */
11403     /**
11404      * @cfg {Boolean} fileUpload
11405      * Set to true if this form is a file upload.
11406      */
11407
11408     /**
11409      * @cfg {Object} baseParams
11410      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11411      */
11412
11413     /**
11414      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11415      */
11416     timeout: 30,
11417     /**
11418      * @cfg {Sting} align (left|right) for navbar forms
11419      */
11420     align : 'left',
11421
11422     // private
11423     activeAction : null,
11424
11425     /**
11426      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11427      * element by passing it or its id or mask the form itself by passing in true.
11428      * @type Mixed
11429      */
11430     waitMsgTarget : false,
11431
11432     loadMask : true,
11433     
11434     /**
11435      * @cfg {Boolean} errorMask (true|false) default false
11436      */
11437     errorMask : false,
11438     
11439     /**
11440      * @cfg {Number} maskOffset Default 100
11441      */
11442     maskOffset : 100,
11443     
11444     /**
11445      * @cfg {Boolean} maskBody
11446      */
11447     maskBody : false,
11448
11449     getAutoCreate : function(){
11450
11451         var cfg = {
11452             tag: 'form',
11453             method : this.method || 'POST',
11454             id : this.id || Roo.id(),
11455             cls : ''
11456         };
11457         if (this.parent().xtype.match(/^Nav/)) {
11458             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11459
11460         }
11461
11462         if (this.labelAlign == 'left' ) {
11463             cfg.cls += ' form-horizontal';
11464         }
11465
11466
11467         return cfg;
11468     },
11469     initEvents : function()
11470     {
11471         this.el.on('submit', this.onSubmit, this);
11472         // this was added as random key presses on the form where triggering form submit.
11473         this.el.on('keypress', function(e) {
11474             if (e.getCharCode() != 13) {
11475                 return true;
11476             }
11477             // we might need to allow it for textareas.. and some other items.
11478             // check e.getTarget().
11479
11480             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11481                 return true;
11482             }
11483
11484             Roo.log("keypress blocked");
11485
11486             e.preventDefault();
11487             return false;
11488         });
11489         
11490     },
11491     // private
11492     onSubmit : function(e){
11493         e.stopEvent();
11494     },
11495
11496      /**
11497      * Returns true if client-side validation on the form is successful.
11498      * @return Boolean
11499      */
11500     isValid : function(){
11501         var items = this.getItems();
11502         var valid = true;
11503         var target = false;
11504         
11505         items.each(function(f){
11506             
11507             if(f.validate()){
11508                 return;
11509             }
11510             
11511             Roo.log('invalid field: ' + f.name);
11512             
11513             valid = false;
11514
11515             if(!target && f.el.isVisible(true)){
11516                 target = f;
11517             }
11518            
11519         });
11520         
11521         if(this.errorMask && !valid){
11522             Roo.bootstrap.form.Form.popover.mask(this, target);
11523         }
11524         
11525         return valid;
11526     },
11527     
11528     /**
11529      * Returns true if any fields in this form have changed since their original load.
11530      * @return Boolean
11531      */
11532     isDirty : function(){
11533         var dirty = false;
11534         var items = this.getItems();
11535         items.each(function(f){
11536            if(f.isDirty()){
11537                dirty = true;
11538                return false;
11539            }
11540            return true;
11541         });
11542         return dirty;
11543     },
11544      /**
11545      * Performs a predefined action (submit or load) or custom actions you define on this form.
11546      * @param {String} actionName The name of the action type
11547      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11548      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11549      * accept other config options):
11550      * <pre>
11551 Property          Type             Description
11552 ----------------  ---------------  ----------------------------------------------------------------------------------
11553 url               String           The url for the action (defaults to the form's url)
11554 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11555 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11556 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11557                                    validate the form on the client (defaults to false)
11558      * </pre>
11559      * @return {BasicForm} this
11560      */
11561     doAction : function(action, options){
11562         if(typeof action == 'string'){
11563             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11564         }
11565         if(this.fireEvent('beforeaction', this, action) !== false){
11566             this.beforeAction(action);
11567             action.run.defer(100, action);
11568         }
11569         return this;
11570     },
11571
11572     // private
11573     beforeAction : function(action){
11574         var o = action.options;
11575         
11576         if(this.loadMask){
11577             
11578             if(this.maskBody){
11579                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11580             } else {
11581                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11582             }
11583         }
11584         // not really supported yet.. ??
11585
11586         //if(this.waitMsgTarget === true){
11587         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11588         //}else if(this.waitMsgTarget){
11589         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11590         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11591         //}else {
11592         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11593        // }
11594
11595     },
11596
11597     // private
11598     afterAction : function(action, success){
11599         this.activeAction = null;
11600         var o = action.options;
11601
11602         if(this.loadMask){
11603             
11604             if(this.maskBody){
11605                 Roo.get(document.body).unmask();
11606             } else {
11607                 this.el.unmask();
11608             }
11609         }
11610         
11611         //if(this.waitMsgTarget === true){
11612 //            this.el.unmask();
11613         //}else if(this.waitMsgTarget){
11614         //    this.waitMsgTarget.unmask();
11615         //}else{
11616         //    Roo.MessageBox.updateProgress(1);
11617         //    Roo.MessageBox.hide();
11618        // }
11619         //
11620         if(success){
11621             if(o.reset){
11622                 this.reset();
11623             }
11624             Roo.callback(o.success, o.scope, [this, action]);
11625             this.fireEvent('actioncomplete', this, action);
11626
11627         }else{
11628
11629             // failure condition..
11630             // we have a scenario where updates need confirming.
11631             // eg. if a locking scenario exists..
11632             // we look for { errors : { needs_confirm : true }} in the response.
11633             if (
11634                 (typeof(action.result) != 'undefined')  &&
11635                 (typeof(action.result.errors) != 'undefined')  &&
11636                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11637            ){
11638                 var _t = this;
11639                 Roo.log("not supported yet");
11640                  /*
11641
11642                 Roo.MessageBox.confirm(
11643                     "Change requires confirmation",
11644                     action.result.errorMsg,
11645                     function(r) {
11646                         if (r != 'yes') {
11647                             return;
11648                         }
11649                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11650                     }
11651
11652                 );
11653                 */
11654
11655
11656                 return;
11657             }
11658
11659             Roo.callback(o.failure, o.scope, [this, action]);
11660             // show an error message if no failed handler is set..
11661             if (!this.hasListener('actionfailed')) {
11662                 Roo.log("need to add dialog support");
11663                 /*
11664                 Roo.MessageBox.alert("Error",
11665                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11666                         action.result.errorMsg :
11667                         "Saving Failed, please check your entries or try again"
11668                 );
11669                 */
11670             }
11671
11672             this.fireEvent('actionfailed', this, action);
11673         }
11674
11675     },
11676     /**
11677      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11678      * @param {String} id The value to search for
11679      * @return Field
11680      */
11681     findField : function(id){
11682         var items = this.getItems();
11683         var field = items.get(id);
11684         if(!field){
11685              items.each(function(f){
11686                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11687                     field = f;
11688                     return false;
11689                 }
11690                 return true;
11691             });
11692         }
11693         return field || null;
11694     },
11695      /**
11696      * Mark fields in this form invalid in bulk.
11697      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11698      * @return {BasicForm} this
11699      */
11700     markInvalid : function(errors){
11701         if(errors instanceof Array){
11702             for(var i = 0, len = errors.length; i < len; i++){
11703                 var fieldError = errors[i];
11704                 var f = this.findField(fieldError.id);
11705                 if(f){
11706                     f.markInvalid(fieldError.msg);
11707                 }
11708             }
11709         }else{
11710             var field, id;
11711             for(id in errors){
11712                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11713                     field.markInvalid(errors[id]);
11714                 }
11715             }
11716         }
11717         //Roo.each(this.childForms || [], function (f) {
11718         //    f.markInvalid(errors);
11719         //});
11720
11721         return this;
11722     },
11723
11724     /**
11725      * Set values for fields in this form in bulk.
11726      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11727      * @return {BasicForm} this
11728      */
11729     setValues : function(values){
11730         if(values instanceof Array){ // array of objects
11731             for(var i = 0, len = values.length; i < len; i++){
11732                 var v = values[i];
11733                 var f = this.findField(v.id);
11734                 if(f){
11735                     f.setValue(v.value);
11736                     if(this.trackResetOnLoad){
11737                         f.originalValue = f.getValue();
11738                     }
11739                 }
11740             }
11741         }else{ // object hash
11742             var field, id;
11743             for(id in values){
11744                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11745
11746                     if (field.setFromData &&
11747                         field.valueField &&
11748                         field.displayField &&
11749                         // combos' with local stores can
11750                         // be queried via setValue()
11751                         // to set their value..
11752                         (field.store && !field.store.isLocal)
11753                         ) {
11754                         // it's a combo
11755                         var sd = { };
11756                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11757                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11758                         field.setFromData(sd);
11759
11760                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11761                         
11762                         field.setFromData(values);
11763                         
11764                     } else {
11765                         field.setValue(values[id]);
11766                     }
11767
11768
11769                     if(this.trackResetOnLoad){
11770                         field.originalValue = field.getValue();
11771                     }
11772                 }
11773             }
11774         }
11775
11776         //Roo.each(this.childForms || [], function (f) {
11777         //    f.setValues(values);
11778         //});
11779
11780         return this;
11781     },
11782
11783     /**
11784      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11785      * they are returned as an array.
11786      * @param {Boolean} asString
11787      * @return {Object}
11788      */
11789     getValues : function(asString){
11790         //if (this.childForms) {
11791             // copy values from the child forms
11792         //    Roo.each(this.childForms, function (f) {
11793         //        this.setValues(f.getValues());
11794         //    }, this);
11795         //}
11796
11797
11798
11799         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11800         if(asString === true){
11801             return fs;
11802         }
11803         return Roo.urlDecode(fs);
11804     },
11805
11806     /**
11807      * Returns the fields in this form as an object with key/value pairs.
11808      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11809      * @return {Object}
11810      */
11811     getFieldValues : function(with_hidden)
11812     {
11813         var items = this.getItems();
11814         var ret = {};
11815         items.each(function(f){
11816             
11817             if (!f.getName()) {
11818                 return;
11819             }
11820             
11821             var v = f.getValue();
11822             
11823             if (f.inputType =='radio') {
11824                 if (typeof(ret[f.getName()]) == 'undefined') {
11825                     ret[f.getName()] = ''; // empty..
11826                 }
11827
11828                 if (!f.el.dom.checked) {
11829                     return;
11830
11831                 }
11832                 v = f.el.dom.value;
11833
11834             }
11835             
11836             if(f.xtype == 'MoneyField'){
11837                 ret[f.currencyName] = f.getCurrency();
11838             }
11839
11840             // not sure if this supported any more..
11841             if ((typeof(v) == 'object') && f.getRawValue) {
11842                 v = f.getRawValue() ; // dates..
11843             }
11844             // combo boxes where name != hiddenName...
11845             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11846                 ret[f.name] = f.getRawValue();
11847             }
11848             ret[f.getName()] = v;
11849         });
11850
11851         return ret;
11852     },
11853
11854     /**
11855      * Clears all invalid messages in this form.
11856      * @return {BasicForm} this
11857      */
11858     clearInvalid : function(){
11859         var items = this.getItems();
11860
11861         items.each(function(f){
11862            f.clearInvalid();
11863         });
11864
11865         return this;
11866     },
11867
11868     /**
11869      * Resets this form.
11870      * @return {BasicForm} this
11871      */
11872     reset : function(){
11873         var items = this.getItems();
11874         items.each(function(f){
11875             f.reset();
11876         });
11877
11878         Roo.each(this.childForms || [], function (f) {
11879             f.reset();
11880         });
11881
11882
11883         return this;
11884     },
11885     
11886     getItems : function()
11887     {
11888         var r=new Roo.util.MixedCollection(false, function(o){
11889             return o.id || (o.id = Roo.id());
11890         });
11891         var iter = function(el) {
11892             if (el.inputEl) {
11893                 r.add(el);
11894             }
11895             if (!el.items) {
11896                 return;
11897             }
11898             Roo.each(el.items,function(e) {
11899                 iter(e);
11900             });
11901         };
11902
11903         iter(this);
11904         return r;
11905     },
11906     
11907     hideFields : function(items)
11908     {
11909         Roo.each(items, function(i){
11910             
11911             var f = this.findField(i);
11912             
11913             if(!f){
11914                 return;
11915             }
11916             
11917             f.hide();
11918             
11919         }, this);
11920     },
11921     
11922     showFields : function(items)
11923     {
11924         Roo.each(items, function(i){
11925             
11926             var f = this.findField(i);
11927             
11928             if(!f){
11929                 return;
11930             }
11931             
11932             f.show();
11933             
11934         }, this);
11935     }
11936
11937 });
11938
11939 Roo.apply(Roo.bootstrap.form.Form, {
11940     
11941     popover : {
11942         
11943         padding : 5,
11944         
11945         isApplied : false,
11946         
11947         isMasked : false,
11948         
11949         form : false,
11950         
11951         target : false,
11952         
11953         toolTip : false,
11954         
11955         intervalID : false,
11956         
11957         maskEl : false,
11958         
11959         apply : function()
11960         {
11961             if(this.isApplied){
11962                 return;
11963             }
11964             
11965             this.maskEl = {
11966                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11967                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11968                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11969                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11970             };
11971             
11972             this.maskEl.top.enableDisplayMode("block");
11973             this.maskEl.left.enableDisplayMode("block");
11974             this.maskEl.bottom.enableDisplayMode("block");
11975             this.maskEl.right.enableDisplayMode("block");
11976             
11977             this.toolTip = new Roo.bootstrap.Tooltip({
11978                 cls : 'roo-form-error-popover',
11979                 alignment : {
11980                     'left' : ['r-l', [-2,0], 'right'],
11981                     'right' : ['l-r', [2,0], 'left'],
11982                     'bottom' : ['tl-bl', [0,2], 'top'],
11983                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11984                 }
11985             });
11986             
11987             this.toolTip.render(Roo.get(document.body));
11988
11989             this.toolTip.el.enableDisplayMode("block");
11990             
11991             Roo.get(document.body).on('click', function(){
11992                 this.unmask();
11993             }, this);
11994             
11995             Roo.get(document.body).on('touchstart', function(){
11996                 this.unmask();
11997             }, this);
11998             
11999             this.isApplied = true
12000         },
12001         
12002         mask : function(form, target)
12003         {
12004             this.form = form;
12005             
12006             this.target = target;
12007             
12008             if(!this.form.errorMask || !target.el){
12009                 return;
12010             }
12011             
12012             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12013             
12014             Roo.log(scrollable);
12015             
12016             var ot = this.target.el.calcOffsetsTo(scrollable);
12017             
12018             var scrollTo = ot[1] - this.form.maskOffset;
12019             
12020             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12021             
12022             scrollable.scrollTo('top', scrollTo);
12023             
12024             var box = this.target.el.getBox();
12025             Roo.log(box);
12026             var zIndex = Roo.bootstrap.Modal.zIndex++;
12027
12028             
12029             this.maskEl.top.setStyle('position', 'absolute');
12030             this.maskEl.top.setStyle('z-index', zIndex);
12031             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12032             this.maskEl.top.setLeft(0);
12033             this.maskEl.top.setTop(0);
12034             this.maskEl.top.show();
12035             
12036             this.maskEl.left.setStyle('position', 'absolute');
12037             this.maskEl.left.setStyle('z-index', zIndex);
12038             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12039             this.maskEl.left.setLeft(0);
12040             this.maskEl.left.setTop(box.y - this.padding);
12041             this.maskEl.left.show();
12042
12043             this.maskEl.bottom.setStyle('position', 'absolute');
12044             this.maskEl.bottom.setStyle('z-index', zIndex);
12045             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12046             this.maskEl.bottom.setLeft(0);
12047             this.maskEl.bottom.setTop(box.bottom + this.padding);
12048             this.maskEl.bottom.show();
12049
12050             this.maskEl.right.setStyle('position', 'absolute');
12051             this.maskEl.right.setStyle('z-index', zIndex);
12052             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12053             this.maskEl.right.setLeft(box.right + this.padding);
12054             this.maskEl.right.setTop(box.y - this.padding);
12055             this.maskEl.right.show();
12056
12057             this.toolTip.bindEl = this.target.el;
12058
12059             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12060
12061             var tip = this.target.blankText;
12062
12063             if(this.target.getValue() !== '' ) {
12064                 
12065                 if (this.target.invalidText.length) {
12066                     tip = this.target.invalidText;
12067                 } else if (this.target.regexText.length){
12068                     tip = this.target.regexText;
12069                 }
12070             }
12071
12072             this.toolTip.show(tip);
12073
12074             this.intervalID = window.setInterval(function() {
12075                 Roo.bootstrap.form.Form.popover.unmask();
12076             }, 10000);
12077
12078             window.onwheel = function(){ return false;};
12079             
12080             (function(){ this.isMasked = true; }).defer(500, this);
12081             
12082         },
12083         
12084         unmask : function()
12085         {
12086             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12087                 return;
12088             }
12089             
12090             this.maskEl.top.setStyle('position', 'absolute');
12091             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12092             this.maskEl.top.hide();
12093
12094             this.maskEl.left.setStyle('position', 'absolute');
12095             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12096             this.maskEl.left.hide();
12097
12098             this.maskEl.bottom.setStyle('position', 'absolute');
12099             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12100             this.maskEl.bottom.hide();
12101
12102             this.maskEl.right.setStyle('position', 'absolute');
12103             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12104             this.maskEl.right.hide();
12105             
12106             this.toolTip.hide();
12107             
12108             this.toolTip.el.hide();
12109             
12110             window.onwheel = function(){ return true;};
12111             
12112             if(this.intervalID){
12113                 window.clearInterval(this.intervalID);
12114                 this.intervalID = false;
12115             }
12116             
12117             this.isMasked = false;
12118             
12119         }
12120         
12121     }
12122     
12123 });
12124
12125 /*
12126  * Based on:
12127  * Ext JS Library 1.1.1
12128  * Copyright(c) 2006-2007, Ext JS, LLC.
12129  *
12130  * Originally Released Under LGPL - original licence link has changed is not relivant.
12131  *
12132  * Fork - LGPL
12133  * <script type="text/javascript">
12134  */
12135 /**
12136  * @class Roo.form.VTypes
12137  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12138  * @static
12139  */
12140 Roo.form.VTypes = function(){
12141     // closure these in so they are only created once.
12142     var alpha = /^[a-zA-Z_]+$/;
12143     var alphanum = /^[a-zA-Z0-9_]+$/;
12144     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12145     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12146
12147     // All these messages and functions are configurable
12148     return {
12149         /**
12150          * The function used to validate email addresses
12151          * @param {String} value The email address
12152          */
12153         'email' : function(v){
12154             return email.test(v);
12155         },
12156         /**
12157          * The error text to display when the email validation function returns false
12158          * @type String
12159          */
12160         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12161         /**
12162          * The keystroke filter mask to be applied on email input
12163          * @type RegExp
12164          */
12165         'emailMask' : /[a-z0-9_\.\-@]/i,
12166
12167         /**
12168          * The function used to validate URLs
12169          * @param {String} value The URL
12170          */
12171         'url' : function(v){
12172             return url.test(v);
12173         },
12174         /**
12175          * The error text to display when the url validation function returns false
12176          * @type String
12177          */
12178         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12179         
12180         /**
12181          * The function used to validate alpha values
12182          * @param {String} value The value
12183          */
12184         'alpha' : function(v){
12185             return alpha.test(v);
12186         },
12187         /**
12188          * The error text to display when the alpha validation function returns false
12189          * @type String
12190          */
12191         'alphaText' : 'This field should only contain letters and _',
12192         /**
12193          * The keystroke filter mask to be applied on alpha input
12194          * @type RegExp
12195          */
12196         'alphaMask' : /[a-z_]/i,
12197
12198         /**
12199          * The function used to validate alphanumeric values
12200          * @param {String} value The value
12201          */
12202         'alphanum' : function(v){
12203             return alphanum.test(v);
12204         },
12205         /**
12206          * The error text to display when the alphanumeric validation function returns false
12207          * @type String
12208          */
12209         'alphanumText' : 'This field should only contain letters, numbers and _',
12210         /**
12211          * The keystroke filter mask to be applied on alphanumeric input
12212          * @type RegExp
12213          */
12214         'alphanumMask' : /[a-z0-9_]/i
12215     };
12216 }();/*
12217  * - LGPL
12218  *
12219  * Input
12220  * 
12221  */
12222
12223 /**
12224  * @class Roo.bootstrap.form.Input
12225  * @extends Roo.bootstrap.Component
12226  * Bootstrap Input class
12227  * @cfg {Boolean} disabled is it disabled
12228  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12229  * @cfg {String} name name of the input
12230  * @cfg {string} fieldLabel - the label associated
12231  * @cfg {string} placeholder - placeholder to put in text.
12232  * @cfg {string} before - input group add on before
12233  * @cfg {string} after - input group add on after
12234  * @cfg {string} size - (lg|sm) or leave empty..
12235  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12236  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12237  * @cfg {Number} md colspan out of 12 for computer-sized screens
12238  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12239  * @cfg {string} value default value of the input
12240  * @cfg {Number} labelWidth set the width of label 
12241  * @cfg {Number} labellg set the width of label (1-12)
12242  * @cfg {Number} labelmd set the width of label (1-12)
12243  * @cfg {Number} labelsm set the width of label (1-12)
12244  * @cfg {Number} labelxs set the width of label (1-12)
12245  * @cfg {String} labelAlign (top|left)
12246  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12247  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12248  * @cfg {String} indicatorpos (left|right) default left
12249  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12250  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12251  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12252  * @cfg {Roo.bootstrap.Button} before Button to show before
12253  * @cfg {Roo.bootstrap.Button} afterButton to show before
12254  * @cfg {String} align (left|center|right) Default left
12255  * @cfg {Boolean} forceFeedback (true|false) Default false
12256  * 
12257  * @constructor
12258  * Create a new Input
12259  * @param {Object} config The config object
12260  */
12261
12262 Roo.bootstrap.form.Input = function(config){
12263     
12264     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12265     
12266     this.addEvents({
12267         /**
12268          * @event focus
12269          * Fires when this field receives input focus.
12270          * @param {Roo.form.Field} this
12271          */
12272         focus : true,
12273         /**
12274          * @event blur
12275          * Fires when this field loses input focus.
12276          * @param {Roo.form.Field} this
12277          */
12278         blur : true,
12279         /**
12280          * @event specialkey
12281          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12282          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12283          * @param {Roo.form.Field} this
12284          * @param {Roo.EventObject} e The event object
12285          */
12286         specialkey : true,
12287         /**
12288          * @event change
12289          * Fires just before the field blurs if the field value has changed.
12290          * @param {Roo.form.Field} this
12291          * @param {Mixed} newValue The new value
12292          * @param {Mixed} oldValue The original value
12293          */
12294         change : true,
12295         /**
12296          * @event invalid
12297          * Fires after the field has been marked as invalid.
12298          * @param {Roo.form.Field} this
12299          * @param {String} msg The validation message
12300          */
12301         invalid : true,
12302         /**
12303          * @event valid
12304          * Fires after the field has been validated with no errors.
12305          * @param {Roo.form.Field} this
12306          */
12307         valid : true,
12308          /**
12309          * @event keyup
12310          * Fires after the key up
12311          * @param {Roo.form.Field} this
12312          * @param {Roo.EventObject}  e The event Object
12313          */
12314         keyup : true,
12315         /**
12316          * @event paste
12317          * Fires after the user pastes into input
12318          * @param {Roo.form.Field} this
12319          * @param {Roo.EventObject}  e The event Object
12320          */
12321         paste : true
12322     });
12323 };
12324
12325 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12326      /**
12327      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12328       automatic validation (defaults to "keyup").
12329      */
12330     validationEvent : "keyup",
12331      /**
12332      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12333      */
12334     validateOnBlur : true,
12335     /**
12336      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12337      */
12338     validationDelay : 250,
12339      /**
12340      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12341      */
12342     focusClass : "x-form-focus",  // not needed???
12343     
12344        
12345     /**
12346      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12347      */
12348     invalidClass : "has-warning",
12349     
12350     /**
12351      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12352      */
12353     validClass : "has-success",
12354     
12355     /**
12356      * @cfg {Boolean} hasFeedback (true|false) default true
12357      */
12358     hasFeedback : true,
12359     
12360     /**
12361      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12362      */
12363     invalidFeedbackClass : "glyphicon-warning-sign",
12364     
12365     /**
12366      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12367      */
12368     validFeedbackClass : "glyphicon-ok",
12369     
12370     /**
12371      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12372      */
12373     selectOnFocus : false,
12374     
12375      /**
12376      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12377      */
12378     maskRe : null,
12379        /**
12380      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12381      */
12382     vtype : null,
12383     
12384       /**
12385      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12386      */
12387     disableKeyFilter : false,
12388     
12389        /**
12390      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12391      */
12392     disabled : false,
12393      /**
12394      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12395      */
12396     allowBlank : true,
12397     /**
12398      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12399      */
12400     blankText : "Please complete this mandatory field",
12401     
12402      /**
12403      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12404      */
12405     minLength : 0,
12406     /**
12407      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12408      */
12409     maxLength : Number.MAX_VALUE,
12410     /**
12411      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12412      */
12413     minLengthText : "The minimum length for this field is {0}",
12414     /**
12415      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12416      */
12417     maxLengthText : "The maximum length for this field is {0}",
12418   
12419     
12420     /**
12421      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12422      * If available, this function will be called only after the basic validators all return true, and will be passed the
12423      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12424      */
12425     validator : null,
12426     /**
12427      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12428      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12429      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12430      */
12431     regex : null,
12432     /**
12433      * @cfg {String} regexText -- Depricated - use Invalid Text
12434      */
12435     regexText : "",
12436     
12437     /**
12438      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12439      */
12440     invalidText : "",
12441     
12442     
12443     
12444     autocomplete: false,
12445     
12446     
12447     fieldLabel : '',
12448     inputType : 'text',
12449     
12450     name : false,
12451     placeholder: false,
12452     before : false,
12453     after : false,
12454     size : false,
12455     hasFocus : false,
12456     preventMark: false,
12457     isFormField : true,
12458     value : '',
12459     labelWidth : 2,
12460     labelAlign : false,
12461     readOnly : false,
12462     align : false,
12463     formatedValue : false,
12464     forceFeedback : false,
12465     
12466     indicatorpos : 'left',
12467     
12468     labellg : 0,
12469     labelmd : 0,
12470     labelsm : 0,
12471     labelxs : 0,
12472     
12473     capture : '',
12474     accept : '',
12475     
12476     parentLabelAlign : function()
12477     {
12478         var parent = this;
12479         while (parent.parent()) {
12480             parent = parent.parent();
12481             if (typeof(parent.labelAlign) !='undefined') {
12482                 return parent.labelAlign;
12483             }
12484         }
12485         return 'left';
12486         
12487     },
12488     
12489     getAutoCreate : function()
12490     {
12491         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12492         
12493         var id = Roo.id();
12494         
12495         var cfg = {};
12496         
12497         if(this.inputType != 'hidden'){
12498             cfg.cls = 'form-group' //input-group
12499         }
12500         
12501         var input =  {
12502             tag: 'input',
12503             id : id,
12504             type : this.inputType,
12505             value : this.value,
12506             cls : 'form-control',
12507             placeholder : this.placeholder || '',
12508             autocomplete : this.autocomplete || 'new-password'
12509         };
12510         if (this.inputType == 'file') {
12511             input.style = 'overflow:hidden'; // why not in CSS?
12512         }
12513         
12514         if(this.capture.length){
12515             input.capture = this.capture;
12516         }
12517         
12518         if(this.accept.length){
12519             input.accept = this.accept + "/*";
12520         }
12521         
12522         if(this.align){
12523             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12524         }
12525         
12526         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12527             input.maxLength = this.maxLength;
12528         }
12529         
12530         if (this.disabled) {
12531             input.disabled=true;
12532         }
12533         
12534         if (this.readOnly) {
12535             input.readonly=true;
12536         }
12537         
12538         if (this.name) {
12539             input.name = this.name;
12540         }
12541         
12542         if (this.size) {
12543             input.cls += ' input-' + this.size;
12544         }
12545         
12546         var settings=this;
12547         ['xs','sm','md','lg'].map(function(size){
12548             if (settings[size]) {
12549                 cfg.cls += ' col-' + size + '-' + settings[size];
12550             }
12551         });
12552         
12553         var inputblock = input;
12554         
12555         var feedback = {
12556             tag: 'span',
12557             cls: 'glyphicon form-control-feedback'
12558         };
12559             
12560         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12561             
12562             inputblock = {
12563                 cls : 'has-feedback',
12564                 cn :  [
12565                     input,
12566                     feedback
12567                 ] 
12568             };  
12569         }
12570         
12571         if (this.before || this.after) {
12572             
12573             inputblock = {
12574                 cls : 'input-group',
12575                 cn :  [] 
12576             };
12577             
12578             if (this.before && typeof(this.before) == 'string') {
12579                 
12580                 inputblock.cn.push({
12581                     tag :'span',
12582                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12583                     html : this.before
12584                 });
12585             }
12586             if (this.before && typeof(this.before) == 'object') {
12587                 this.before = Roo.factory(this.before);
12588                 
12589                 inputblock.cn.push({
12590                     tag :'span',
12591                     cls : 'roo-input-before input-group-prepend   input-group-' +
12592                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12593                 });
12594             }
12595             
12596             inputblock.cn.push(input);
12597             
12598             if (this.after && typeof(this.after) == 'string') {
12599                 inputblock.cn.push({
12600                     tag :'span',
12601                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12602                     html : this.after
12603                 });
12604             }
12605             if (this.after && typeof(this.after) == 'object') {
12606                 this.after = Roo.factory(this.after);
12607                 
12608                 inputblock.cn.push({
12609                     tag :'span',
12610                     cls : 'roo-input-after input-group-append  input-group-' +
12611                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12612                 });
12613             }
12614             
12615             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12616                 inputblock.cls += ' has-feedback';
12617                 inputblock.cn.push(feedback);
12618             }
12619         };
12620         var indicator = {
12621             tag : 'i',
12622             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12623             tooltip : 'This field is required'
12624         };
12625         if (this.allowBlank ) {
12626             indicator.style = this.allowBlank ? ' display:none' : '';
12627         }
12628         if (align ==='left' && this.fieldLabel.length) {
12629             
12630             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12631             
12632             cfg.cn = [
12633                 indicator,
12634                 {
12635                     tag: 'label',
12636                     'for' :  id,
12637                     cls : 'control-label col-form-label',
12638                     html : this.fieldLabel
12639
12640                 },
12641                 {
12642                     cls : "", 
12643                     cn: [
12644                         inputblock
12645                     ]
12646                 }
12647             ];
12648             
12649             var labelCfg = cfg.cn[1];
12650             var contentCfg = cfg.cn[2];
12651             
12652             if(this.indicatorpos == 'right'){
12653                 cfg.cn = [
12654                     {
12655                         tag: 'label',
12656                         'for' :  id,
12657                         cls : 'control-label col-form-label',
12658                         cn : [
12659                             {
12660                                 tag : 'span',
12661                                 html : this.fieldLabel
12662                             },
12663                             indicator
12664                         ]
12665                     },
12666                     {
12667                         cls : "",
12668                         cn: [
12669                             inputblock
12670                         ]
12671                     }
12672
12673                 ];
12674                 
12675                 labelCfg = cfg.cn[0];
12676                 contentCfg = cfg.cn[1];
12677             
12678             }
12679             
12680             if(this.labelWidth > 12){
12681                 labelCfg.style = "width: " + this.labelWidth + 'px';
12682             }
12683             
12684             if(this.labelWidth < 13 && this.labelmd == 0){
12685                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12686             }
12687             
12688             if(this.labellg > 0){
12689                 labelCfg.cls += ' col-lg-' + this.labellg;
12690                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12691             }
12692             
12693             if(this.labelmd > 0){
12694                 labelCfg.cls += ' col-md-' + this.labelmd;
12695                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12696             }
12697             
12698             if(this.labelsm > 0){
12699                 labelCfg.cls += ' col-sm-' + this.labelsm;
12700                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12701             }
12702             
12703             if(this.labelxs > 0){
12704                 labelCfg.cls += ' col-xs-' + this.labelxs;
12705                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12706             }
12707             
12708             
12709         } else if ( this.fieldLabel.length) {
12710                 
12711             
12712             
12713             cfg.cn = [
12714                 {
12715                     tag : 'i',
12716                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12717                     tooltip : 'This field is required',
12718                     style : this.allowBlank ? ' display:none' : '' 
12719                 },
12720                 {
12721                     tag: 'label',
12722                    //cls : 'input-group-addon',
12723                     html : this.fieldLabel
12724
12725                 },
12726
12727                inputblock
12728
12729            ];
12730            
12731            if(this.indicatorpos == 'right'){
12732        
12733                 cfg.cn = [
12734                     {
12735                         tag: 'label',
12736                        //cls : 'input-group-addon',
12737                         html : this.fieldLabel
12738
12739                     },
12740                     {
12741                         tag : 'i',
12742                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12743                         tooltip : 'This field is required',
12744                         style : this.allowBlank ? ' display:none' : '' 
12745                     },
12746
12747                    inputblock
12748
12749                ];
12750
12751             }
12752
12753         } else {
12754             
12755             cfg.cn = [
12756
12757                     inputblock
12758
12759             ];
12760                 
12761                 
12762         };
12763         
12764         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12765            cfg.cls += ' navbar-form';
12766         }
12767         
12768         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12769             // on BS4 we do this only if not form 
12770             cfg.cls += ' navbar-form';
12771             cfg.tag = 'li';
12772         }
12773         
12774         return cfg;
12775         
12776     },
12777     /**
12778      * return the real input element.
12779      */
12780     inputEl: function ()
12781     {
12782         return this.el.select('input.form-control',true).first();
12783     },
12784     
12785     tooltipEl : function()
12786     {
12787         return this.inputEl();
12788     },
12789     
12790     indicatorEl : function()
12791     {
12792         if (Roo.bootstrap.version == 4) {
12793             return false; // not enabled in v4 yet.
12794         }
12795         
12796         var indicator = this.el.select('i.roo-required-indicator',true).first();
12797         
12798         if(!indicator){
12799             return false;
12800         }
12801         
12802         return indicator;
12803         
12804     },
12805     
12806     setDisabled : function(v)
12807     {
12808         var i  = this.inputEl().dom;
12809         if (!v) {
12810             i.removeAttribute('disabled');
12811             return;
12812             
12813         }
12814         i.setAttribute('disabled','true');
12815     },
12816     initEvents : function()
12817     {
12818           
12819         this.inputEl().on("keydown" , this.fireKey,  this);
12820         this.inputEl().on("focus", this.onFocus,  this);
12821         this.inputEl().on("blur", this.onBlur,  this);
12822         
12823         this.inputEl().relayEvent('keyup', this);
12824         this.inputEl().relayEvent('paste', this);
12825         
12826         this.indicator = this.indicatorEl();
12827         
12828         if(this.indicator){
12829             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12830         }
12831  
12832         // reference to original value for reset
12833         this.originalValue = this.getValue();
12834         //Roo.form.TextField.superclass.initEvents.call(this);
12835         if(this.validationEvent == 'keyup'){
12836             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12837             this.inputEl().on('keyup', this.filterValidation, this);
12838         }
12839         else if(this.validationEvent !== false){
12840             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12841         }
12842         
12843         if(this.selectOnFocus){
12844             this.on("focus", this.preFocus, this);
12845             
12846         }
12847         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12848             this.inputEl().on("keypress", this.filterKeys, this);
12849         } else {
12850             this.inputEl().relayEvent('keypress', this);
12851         }
12852        /* if(this.grow){
12853             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12854             this.el.on("click", this.autoSize,  this);
12855         }
12856         */
12857         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12858             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12859         }
12860         
12861         if (typeof(this.before) == 'object') {
12862             this.before.render(this.el.select('.roo-input-before',true).first());
12863         }
12864         if (typeof(this.after) == 'object') {
12865             this.after.render(this.el.select('.roo-input-after',true).first());
12866         }
12867         
12868         this.inputEl().on('change', this.onChange, this);
12869         
12870     },
12871     filterValidation : function(e){
12872         if(!e.isNavKeyPress()){
12873             this.validationTask.delay(this.validationDelay);
12874         }
12875     },
12876      /**
12877      * Validates the field value
12878      * @return {Boolean} True if the value is valid, else false
12879      */
12880     validate : function(){
12881         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12882         if(this.disabled || this.validateValue(this.getRawValue())){
12883             this.markValid();
12884             return true;
12885         }
12886         
12887         this.markInvalid();
12888         return false;
12889     },
12890     
12891     
12892     /**
12893      * Validates a value according to the field's validation rules and marks the field as invalid
12894      * if the validation fails
12895      * @param {Mixed} value The value to validate
12896      * @return {Boolean} True if the value is valid, else false
12897      */
12898     validateValue : function(value)
12899     {
12900         if(this.getVisibilityEl().hasClass('hidden')){
12901             return true;
12902         }
12903         
12904         if(value.length < 1)  { // if it's blank
12905             if(this.allowBlank){
12906                 return true;
12907             }
12908             return false;
12909         }
12910         
12911         if(value.length < this.minLength){
12912             return false;
12913         }
12914         if(value.length > this.maxLength){
12915             return false;
12916         }
12917         if(this.vtype){
12918             var vt = Roo.form.VTypes;
12919             if(!vt[this.vtype](value, this)){
12920                 return false;
12921             }
12922         }
12923         if(typeof this.validator == "function"){
12924             var msg = this.validator(value);
12925             if(msg !== true){
12926                 return false;
12927             }
12928             if (typeof(msg) == 'string') {
12929                 this.invalidText = msg;
12930             }
12931         }
12932         
12933         if(this.regex && !this.regex.test(value)){
12934             return false;
12935         }
12936         
12937         return true;
12938     },
12939     
12940      // private
12941     fireKey : function(e){
12942         //Roo.log('field ' + e.getKey());
12943         if(e.isNavKeyPress()){
12944             this.fireEvent("specialkey", this, e);
12945         }
12946     },
12947     focus : function (selectText){
12948         if(this.rendered){
12949             this.inputEl().focus();
12950             if(selectText === true){
12951                 this.inputEl().dom.select();
12952             }
12953         }
12954         return this;
12955     } ,
12956     
12957     onFocus : function(){
12958         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12959            // this.el.addClass(this.focusClass);
12960         }
12961         if(!this.hasFocus){
12962             this.hasFocus = true;
12963             this.startValue = this.getValue();
12964             this.fireEvent("focus", this);
12965         }
12966     },
12967     
12968     beforeBlur : Roo.emptyFn,
12969
12970     
12971     // private
12972     onBlur : function(){
12973         this.beforeBlur();
12974         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12975             //this.el.removeClass(this.focusClass);
12976         }
12977         this.hasFocus = false;
12978         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12979             this.validate();
12980         }
12981         var v = this.getValue();
12982         if(String(v) !== String(this.startValue)){
12983             this.fireEvent('change', this, v, this.startValue);
12984         }
12985         this.fireEvent("blur", this);
12986     },
12987     
12988     onChange : function(e)
12989     {
12990         var v = this.getValue();
12991         if(String(v) !== String(this.startValue)){
12992             this.fireEvent('change', this, v, this.startValue);
12993         }
12994         
12995     },
12996     
12997     /**
12998      * Resets the current field value to the originally loaded value and clears any validation messages
12999      */
13000     reset : function(){
13001         this.setValue(this.originalValue);
13002         this.validate();
13003     },
13004      /**
13005      * Returns the name of the field
13006      * @return {Mixed} name The name field
13007      */
13008     getName: function(){
13009         return this.name;
13010     },
13011      /**
13012      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13013      * @return {Mixed} value The field value
13014      */
13015     getValue : function(){
13016         
13017         var v = this.inputEl().getValue();
13018         
13019         return v;
13020     },
13021     /**
13022      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13023      * @return {Mixed} value The field value
13024      */
13025     getRawValue : function(){
13026         var v = this.inputEl().getValue();
13027         
13028         return v;
13029     },
13030     
13031     /**
13032      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13033      * @param {Mixed} value The value to set
13034      */
13035     setRawValue : function(v){
13036         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13037     },
13038     
13039     selectText : function(start, end){
13040         var v = this.getRawValue();
13041         if(v.length > 0){
13042             start = start === undefined ? 0 : start;
13043             end = end === undefined ? v.length : end;
13044             var d = this.inputEl().dom;
13045             if(d.setSelectionRange){
13046                 d.setSelectionRange(start, end);
13047             }else if(d.createTextRange){
13048                 var range = d.createTextRange();
13049                 range.moveStart("character", start);
13050                 range.moveEnd("character", v.length-end);
13051                 range.select();
13052             }
13053         }
13054     },
13055     
13056     /**
13057      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13058      * @param {Mixed} value The value to set
13059      */
13060     setValue : function(v){
13061         this.value = v;
13062         if(this.rendered){
13063             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13064             this.validate();
13065         }
13066     },
13067     
13068     /*
13069     processValue : function(value){
13070         if(this.stripCharsRe){
13071             var newValue = value.replace(this.stripCharsRe, '');
13072             if(newValue !== value){
13073                 this.setRawValue(newValue);
13074                 return newValue;
13075             }
13076         }
13077         return value;
13078     },
13079   */
13080     preFocus : function(){
13081         
13082         if(this.selectOnFocus){
13083             this.inputEl().dom.select();
13084         }
13085     },
13086     filterKeys : function(e){
13087         var k = e.getKey();
13088         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13089             return;
13090         }
13091         var c = e.getCharCode(), cc = String.fromCharCode(c);
13092         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13093             return;
13094         }
13095         if(!this.maskRe.test(cc)){
13096             e.stopEvent();
13097         }
13098     },
13099      /**
13100      * Clear any invalid styles/messages for this field
13101      */
13102     clearInvalid : function(){
13103         
13104         if(!this.el || this.preventMark){ // not rendered
13105             return;
13106         }
13107         
13108         
13109         this.el.removeClass([this.invalidClass, 'is-invalid']);
13110         
13111         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13112             
13113             var feedback = this.el.select('.form-control-feedback', true).first();
13114             
13115             if(feedback){
13116                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13117             }
13118             
13119         }
13120         
13121         if(this.indicator){
13122             this.indicator.removeClass('visible');
13123             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13124         }
13125         
13126         this.fireEvent('valid', this);
13127     },
13128     
13129      /**
13130      * Mark this field as valid
13131      */
13132     markValid : function()
13133     {
13134         if(!this.el  || this.preventMark){ // not rendered...
13135             return;
13136         }
13137         
13138         this.el.removeClass([this.invalidClass, this.validClass]);
13139         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13140
13141         var feedback = this.el.select('.form-control-feedback', true).first();
13142             
13143         if(feedback){
13144             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13145         }
13146         
13147         if(this.indicator){
13148             this.indicator.removeClass('visible');
13149             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13150         }
13151         
13152         if(this.disabled){
13153             return;
13154         }
13155         
13156            
13157         if(this.allowBlank && !this.getRawValue().length){
13158             return;
13159         }
13160         if (Roo.bootstrap.version == 3) {
13161             this.el.addClass(this.validClass);
13162         } else {
13163             this.inputEl().addClass('is-valid');
13164         }
13165
13166         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13167             
13168             var feedback = this.el.select('.form-control-feedback', true).first();
13169             
13170             if(feedback){
13171                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13172                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13173             }
13174             
13175         }
13176         
13177         this.fireEvent('valid', this);
13178     },
13179     
13180      /**
13181      * Mark this field as invalid
13182      * @param {String} msg The validation message
13183      */
13184     markInvalid : function(msg)
13185     {
13186         if(!this.el  || this.preventMark){ // not rendered
13187             return;
13188         }
13189         
13190         this.el.removeClass([this.invalidClass, this.validClass]);
13191         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13192         
13193         var feedback = this.el.select('.form-control-feedback', true).first();
13194             
13195         if(feedback){
13196             this.el.select('.form-control-feedback', true).first().removeClass(
13197                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13198         }
13199
13200         if(this.disabled){
13201             return;
13202         }
13203         
13204         if(this.allowBlank && !this.getRawValue().length){
13205             return;
13206         }
13207         
13208         if(this.indicator){
13209             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13210             this.indicator.addClass('visible');
13211         }
13212         if (Roo.bootstrap.version == 3) {
13213             this.el.addClass(this.invalidClass);
13214         } else {
13215             this.inputEl().addClass('is-invalid');
13216         }
13217         
13218         
13219         
13220         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13221             
13222             var feedback = this.el.select('.form-control-feedback', true).first();
13223             
13224             if(feedback){
13225                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13226                 
13227                 if(this.getValue().length || this.forceFeedback){
13228                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13229                 }
13230                 
13231             }
13232             
13233         }
13234         
13235         this.fireEvent('invalid', this, msg);
13236     },
13237     // private
13238     SafariOnKeyDown : function(event)
13239     {
13240         // this is a workaround for a password hang bug on chrome/ webkit.
13241         if (this.inputEl().dom.type != 'password') {
13242             return;
13243         }
13244         
13245         var isSelectAll = false;
13246         
13247         if(this.inputEl().dom.selectionEnd > 0){
13248             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13249         }
13250         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13251             event.preventDefault();
13252             this.setValue('');
13253             return;
13254         }
13255         
13256         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13257             
13258             event.preventDefault();
13259             // this is very hacky as keydown always get's upper case.
13260             //
13261             var cc = String.fromCharCode(event.getCharCode());
13262             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13263             
13264         }
13265     },
13266     adjustWidth : function(tag, w){
13267         tag = tag.toLowerCase();
13268         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13269             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13270                 if(tag == 'input'){
13271                     return w + 2;
13272                 }
13273                 if(tag == 'textarea'){
13274                     return w-2;
13275                 }
13276             }else if(Roo.isOpera){
13277                 if(tag == 'input'){
13278                     return w + 2;
13279                 }
13280                 if(tag == 'textarea'){
13281                     return w-2;
13282                 }
13283             }
13284         }
13285         return w;
13286     },
13287     
13288     setFieldLabel : function(v)
13289     {
13290         if(!this.rendered){
13291             return;
13292         }
13293         
13294         if(this.indicatorEl()){
13295             var ar = this.el.select('label > span',true);
13296             
13297             if (ar.elements.length) {
13298                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13299                 this.fieldLabel = v;
13300                 return;
13301             }
13302             
13303             var br = this.el.select('label',true);
13304             
13305             if(br.elements.length) {
13306                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13307                 this.fieldLabel = v;
13308                 return;
13309             }
13310             
13311             Roo.log('Cannot Found any of label > span || label in input');
13312             return;
13313         }
13314         
13315         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13316         this.fieldLabel = v;
13317         
13318         
13319     }
13320 });
13321
13322  
13323 /*
13324  * - LGPL
13325  *
13326  * Input
13327  * 
13328  */
13329
13330 /**
13331  * @class Roo.bootstrap.form.TextArea
13332  * @extends Roo.bootstrap.form.Input
13333  * Bootstrap TextArea class
13334  * @cfg {Number} cols Specifies the visible width of a text area
13335  * @cfg {Number} rows Specifies the visible number of lines in a text area
13336  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13337  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13338  * @cfg {string} html text
13339  * 
13340  * @constructor
13341  * Create a new TextArea
13342  * @param {Object} config The config object
13343  */
13344
13345 Roo.bootstrap.form.TextArea = function(config){
13346     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13347    
13348 };
13349
13350 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13351      
13352     cols : false,
13353     rows : 5,
13354     readOnly : false,
13355     warp : 'soft',
13356     resize : false,
13357     value: false,
13358     html: false,
13359     
13360     getAutoCreate : function(){
13361         
13362         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13363         
13364         var id = Roo.id();
13365         
13366         var cfg = {};
13367         
13368         if(this.inputType != 'hidden'){
13369             cfg.cls = 'form-group' //input-group
13370         }
13371         
13372         var input =  {
13373             tag: 'textarea',
13374             id : id,
13375             warp : this.warp,
13376             rows : this.rows,
13377             value : this.value || '',
13378             html: this.html || '',
13379             cls : 'form-control',
13380             placeholder : this.placeholder || '' 
13381             
13382         };
13383         
13384         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13385             input.maxLength = this.maxLength;
13386         }
13387         
13388         if(this.resize){
13389             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13390         }
13391         
13392         if(this.cols){
13393             input.cols = this.cols;
13394         }
13395         
13396         if (this.readOnly) {
13397             input.readonly = true;
13398         }
13399         
13400         if (this.name) {
13401             input.name = this.name;
13402         }
13403         
13404         if (this.size) {
13405             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13406         }
13407         
13408         var settings=this;
13409         ['xs','sm','md','lg'].map(function(size){
13410             if (settings[size]) {
13411                 cfg.cls += ' col-' + size + '-' + settings[size];
13412             }
13413         });
13414         
13415         var inputblock = input;
13416         
13417         if(this.hasFeedback && !this.allowBlank){
13418             
13419             var feedback = {
13420                 tag: 'span',
13421                 cls: 'glyphicon form-control-feedback'
13422             };
13423
13424             inputblock = {
13425                 cls : 'has-feedback',
13426                 cn :  [
13427                     input,
13428                     feedback
13429                 ] 
13430             };  
13431         }
13432         
13433         
13434         if (this.before || this.after) {
13435             
13436             inputblock = {
13437                 cls : 'input-group',
13438                 cn :  [] 
13439             };
13440             if (this.before) {
13441                 inputblock.cn.push({
13442                     tag :'span',
13443                     cls : 'input-group-addon',
13444                     html : this.before
13445                 });
13446             }
13447             
13448             inputblock.cn.push(input);
13449             
13450             if(this.hasFeedback && !this.allowBlank){
13451                 inputblock.cls += ' has-feedback';
13452                 inputblock.cn.push(feedback);
13453             }
13454             
13455             if (this.after) {
13456                 inputblock.cn.push({
13457                     tag :'span',
13458                     cls : 'input-group-addon',
13459                     html : this.after
13460                 });
13461             }
13462             
13463         }
13464         
13465         if (align ==='left' && this.fieldLabel.length) {
13466             cfg.cn = [
13467                 {
13468                     tag: 'label',
13469                     'for' :  id,
13470                     cls : 'control-label',
13471                     html : this.fieldLabel
13472                 },
13473                 {
13474                     cls : "",
13475                     cn: [
13476                         inputblock
13477                     ]
13478                 }
13479
13480             ];
13481             
13482             if(this.labelWidth > 12){
13483                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13484             }
13485
13486             if(this.labelWidth < 13 && this.labelmd == 0){
13487                 this.labelmd = this.labelWidth;
13488             }
13489
13490             if(this.labellg > 0){
13491                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13492                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13493             }
13494
13495             if(this.labelmd > 0){
13496                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13497                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13498             }
13499
13500             if(this.labelsm > 0){
13501                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13502                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13503             }
13504
13505             if(this.labelxs > 0){
13506                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13507                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13508             }
13509             
13510         } else if ( this.fieldLabel.length) {
13511             cfg.cn = [
13512
13513                {
13514                    tag: 'label',
13515                    //cls : 'input-group-addon',
13516                    html : this.fieldLabel
13517
13518                },
13519
13520                inputblock
13521
13522            ];
13523
13524         } else {
13525
13526             cfg.cn = [
13527
13528                 inputblock
13529
13530             ];
13531                 
13532         }
13533         
13534         if (this.disabled) {
13535             input.disabled=true;
13536         }
13537         
13538         return cfg;
13539         
13540     },
13541     /**
13542      * return the real textarea element.
13543      */
13544     inputEl: function ()
13545     {
13546         return this.el.select('textarea.form-control',true).first();
13547     },
13548     
13549     /**
13550      * Clear any invalid styles/messages for this field
13551      */
13552     clearInvalid : function()
13553     {
13554         
13555         if(!this.el || this.preventMark){ // not rendered
13556             return;
13557         }
13558         
13559         var label = this.el.select('label', true).first();
13560         var icon = this.el.select('i.fa-star', true).first();
13561         
13562         if(label && icon){
13563             icon.remove();
13564         }
13565         this.el.removeClass( this.validClass);
13566         this.inputEl().removeClass('is-invalid');
13567          
13568         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13569             
13570             var feedback = this.el.select('.form-control-feedback', true).first();
13571             
13572             if(feedback){
13573                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13574             }
13575             
13576         }
13577         
13578         this.fireEvent('valid', this);
13579     },
13580     
13581      /**
13582      * Mark this field as valid
13583      */
13584     markValid : function()
13585     {
13586         if(!this.el  || this.preventMark){ // not rendered
13587             return;
13588         }
13589         
13590         this.el.removeClass([this.invalidClass, this.validClass]);
13591         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13592         
13593         var feedback = this.el.select('.form-control-feedback', true).first();
13594             
13595         if(feedback){
13596             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13597         }
13598
13599         if(this.disabled || this.allowBlank){
13600             return;
13601         }
13602         
13603         var label = this.el.select('label', true).first();
13604         var icon = this.el.select('i.fa-star', true).first();
13605         
13606         if(label && icon){
13607             icon.remove();
13608         }
13609         if (Roo.bootstrap.version == 3) {
13610             this.el.addClass(this.validClass);
13611         } else {
13612             this.inputEl().addClass('is-valid');
13613         }
13614         
13615         
13616         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13617             
13618             var feedback = this.el.select('.form-control-feedback', true).first();
13619             
13620             if(feedback){
13621                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13622                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13623             }
13624             
13625         }
13626         
13627         this.fireEvent('valid', this);
13628     },
13629     
13630      /**
13631      * Mark this field as invalid
13632      * @param {String} msg The validation message
13633      */
13634     markInvalid : function(msg)
13635     {
13636         if(!this.el  || this.preventMark){ // not rendered
13637             return;
13638         }
13639         
13640         this.el.removeClass([this.invalidClass, this.validClass]);
13641         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13642         
13643         var feedback = this.el.select('.form-control-feedback', true).first();
13644             
13645         if(feedback){
13646             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13647         }
13648
13649         if(this.disabled || this.allowBlank){
13650             return;
13651         }
13652         
13653         var label = this.el.select('label', true).first();
13654         var icon = this.el.select('i.fa-star', true).first();
13655         
13656         if(!this.getValue().length && label && !icon){
13657             this.el.createChild({
13658                 tag : 'i',
13659                 cls : 'text-danger fa fa-lg fa-star',
13660                 tooltip : 'This field is required',
13661                 style : 'margin-right:5px;'
13662             }, label, true);
13663         }
13664         
13665         if (Roo.bootstrap.version == 3) {
13666             this.el.addClass(this.invalidClass);
13667         } else {
13668             this.inputEl().addClass('is-invalid');
13669         }
13670         
13671         // fixme ... this may be depricated need to test..
13672         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13673             
13674             var feedback = this.el.select('.form-control-feedback', true).first();
13675             
13676             if(feedback){
13677                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13678                 
13679                 if(this.getValue().length || this.forceFeedback){
13680                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13681                 }
13682                 
13683             }
13684             
13685         }
13686         
13687         this.fireEvent('invalid', this, msg);
13688     }
13689 });
13690
13691  
13692 /*
13693  * - LGPL
13694  *
13695  * trigger field - base class for combo..
13696  * 
13697  */
13698  
13699 /**
13700  * @class Roo.bootstrap.form.TriggerField
13701  * @extends Roo.bootstrap.form.Input
13702  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13703  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13704  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13705  * for which you can provide a custom implementation.  For example:
13706  * <pre><code>
13707 var trigger = new Roo.bootstrap.form.TriggerField();
13708 trigger.onTriggerClick = myTriggerFn;
13709 trigger.applyTo('my-field');
13710 </code></pre>
13711  *
13712  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13713  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13714  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13715  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13716  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13717
13718  * @constructor
13719  * Create a new TriggerField.
13720  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13721  * to the base TextField)
13722  */
13723 Roo.bootstrap.form.TriggerField = function(config){
13724     this.mimicing = false;
13725     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13726 };
13727
13728 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13729     /**
13730      * @cfg {String} triggerClass A CSS class to apply to the trigger
13731      */
13732      /**
13733      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13734      */
13735     hideTrigger:false,
13736
13737     /**
13738      * @cfg {Boolean} removable (true|false) special filter default false
13739      */
13740     removable : false,
13741     
13742     /** @cfg {Boolean} grow @hide */
13743     /** @cfg {Number} growMin @hide */
13744     /** @cfg {Number} growMax @hide */
13745
13746     /**
13747      * @hide 
13748      * @method
13749      */
13750     autoSize: Roo.emptyFn,
13751     // private
13752     monitorTab : true,
13753     // private
13754     deferHeight : true,
13755
13756     
13757     actionMode : 'wrap',
13758     
13759     caret : false,
13760     
13761     
13762     getAutoCreate : function(){
13763        
13764         var align = this.labelAlign || this.parentLabelAlign();
13765         
13766         var id = Roo.id();
13767         
13768         var cfg = {
13769             cls: 'form-group' //input-group
13770         };
13771         
13772         
13773         var input =  {
13774             tag: 'input',
13775             id : id,
13776             type : this.inputType,
13777             cls : 'form-control',
13778             autocomplete: 'new-password',
13779             placeholder : this.placeholder || '' 
13780             
13781         };
13782         if (this.name) {
13783             input.name = this.name;
13784         }
13785         if (this.size) {
13786             input.cls += ' input-' + this.size;
13787         }
13788         
13789         if (this.disabled) {
13790             input.disabled=true;
13791         }
13792         
13793         var inputblock = input;
13794         
13795         if(this.hasFeedback && !this.allowBlank){
13796             
13797             var feedback = {
13798                 tag: 'span',
13799                 cls: 'glyphicon form-control-feedback'
13800             };
13801             
13802             if(this.removable && !this.editable  ){
13803                 inputblock = {
13804                     cls : 'has-feedback',
13805                     cn :  [
13806                         inputblock,
13807                         {
13808                             tag: 'button',
13809                             html : 'x',
13810                             cls : 'roo-combo-removable-btn close'
13811                         },
13812                         feedback
13813                     ] 
13814                 };
13815             } else {
13816                 inputblock = {
13817                     cls : 'has-feedback',
13818                     cn :  [
13819                         inputblock,
13820                         feedback
13821                     ] 
13822                 };
13823             }
13824
13825         } else {
13826             if(this.removable && !this.editable ){
13827                 inputblock = {
13828                     cls : 'roo-removable',
13829                     cn :  [
13830                         inputblock,
13831                         {
13832                             tag: 'button',
13833                             html : 'x',
13834                             cls : 'roo-combo-removable-btn close'
13835                         }
13836                     ] 
13837                 };
13838             }
13839         }
13840         
13841         if (this.before || this.after) {
13842             
13843             inputblock = {
13844                 cls : 'input-group',
13845                 cn :  [] 
13846             };
13847             if (this.before) {
13848                 inputblock.cn.push({
13849                     tag :'span',
13850                     cls : 'input-group-addon input-group-prepend input-group-text',
13851                     html : this.before
13852                 });
13853             }
13854             
13855             inputblock.cn.push(input);
13856             
13857             if(this.hasFeedback && !this.allowBlank){
13858                 inputblock.cls += ' has-feedback';
13859                 inputblock.cn.push(feedback);
13860             }
13861             
13862             if (this.after) {
13863                 inputblock.cn.push({
13864                     tag :'span',
13865                     cls : 'input-group-addon input-group-append input-group-text',
13866                     html : this.after
13867                 });
13868             }
13869             
13870         };
13871         
13872       
13873         
13874         var ibwrap = inputblock;
13875         
13876         if(this.multiple){
13877             ibwrap = {
13878                 tag: 'ul',
13879                 cls: 'roo-select2-choices',
13880                 cn:[
13881                     {
13882                         tag: 'li',
13883                         cls: 'roo-select2-search-field',
13884                         cn: [
13885
13886                             inputblock
13887                         ]
13888                     }
13889                 ]
13890             };
13891                 
13892         }
13893         
13894         var combobox = {
13895             cls: 'roo-select2-container input-group',
13896             cn: [
13897                  {
13898                     tag: 'input',
13899                     type : 'hidden',
13900                     cls: 'form-hidden-field'
13901                 },
13902                 ibwrap
13903             ]
13904         };
13905         
13906         if(!this.multiple && this.showToggleBtn){
13907             
13908             var caret = {
13909                         tag: 'span',
13910                         cls: 'caret'
13911              };
13912             if (this.caret != false) {
13913                 caret = {
13914                      tag: 'i',
13915                      cls: 'fa fa-' + this.caret
13916                 };
13917                 
13918             }
13919             
13920             combobox.cn.push({
13921                 tag :'span',
13922                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13923                 cn : [
13924                     Roo.bootstrap.version == 3 ? caret : '',
13925                     {
13926                         tag: 'span',
13927                         cls: 'combobox-clear',
13928                         cn  : [
13929                             {
13930                                 tag : 'i',
13931                                 cls: 'icon-remove'
13932                             }
13933                         ]
13934                     }
13935                 ]
13936
13937             })
13938         }
13939         
13940         if(this.multiple){
13941             combobox.cls += ' roo-select2-container-multi';
13942         }
13943          var indicator = {
13944             tag : 'i',
13945             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13946             tooltip : 'This field is required'
13947         };
13948         if (Roo.bootstrap.version == 4) {
13949             indicator = {
13950                 tag : 'i',
13951                 style : 'display:none'
13952             };
13953         }
13954         
13955         
13956         if (align ==='left' && this.fieldLabel.length) {
13957             
13958             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13959
13960             cfg.cn = [
13961                 indicator,
13962                 {
13963                     tag: 'label',
13964                     'for' :  id,
13965                     cls : 'control-label',
13966                     html : this.fieldLabel
13967
13968                 },
13969                 {
13970                     cls : "", 
13971                     cn: [
13972                         combobox
13973                     ]
13974                 }
13975
13976             ];
13977             
13978             var labelCfg = cfg.cn[1];
13979             var contentCfg = cfg.cn[2];
13980             
13981             if(this.indicatorpos == 'right'){
13982                 cfg.cn = [
13983                     {
13984                         tag: 'label',
13985                         'for' :  id,
13986                         cls : 'control-label',
13987                         cn : [
13988                             {
13989                                 tag : 'span',
13990                                 html : this.fieldLabel
13991                             },
13992                             indicator
13993                         ]
13994                     },
13995                     {
13996                         cls : "", 
13997                         cn: [
13998                             combobox
13999                         ]
14000                     }
14001
14002                 ];
14003                 
14004                 labelCfg = cfg.cn[0];
14005                 contentCfg = cfg.cn[1];
14006             }
14007             
14008             if(this.labelWidth > 12){
14009                 labelCfg.style = "width: " + this.labelWidth + 'px';
14010             }
14011             
14012             if(this.labelWidth < 13 && this.labelmd == 0){
14013                 this.labelmd = this.labelWidth;
14014             }
14015             
14016             if(this.labellg > 0){
14017                 labelCfg.cls += ' col-lg-' + this.labellg;
14018                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14019             }
14020             
14021             if(this.labelmd > 0){
14022                 labelCfg.cls += ' col-md-' + this.labelmd;
14023                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14024             }
14025             
14026             if(this.labelsm > 0){
14027                 labelCfg.cls += ' col-sm-' + this.labelsm;
14028                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14029             }
14030             
14031             if(this.labelxs > 0){
14032                 labelCfg.cls += ' col-xs-' + this.labelxs;
14033                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14034             }
14035             
14036         } else if ( this.fieldLabel.length) {
14037 //                Roo.log(" label");
14038             cfg.cn = [
14039                 indicator,
14040                {
14041                    tag: 'label',
14042                    //cls : 'input-group-addon',
14043                    html : this.fieldLabel
14044
14045                },
14046
14047                combobox
14048
14049             ];
14050             
14051             if(this.indicatorpos == 'right'){
14052                 
14053                 cfg.cn = [
14054                     {
14055                        tag: 'label',
14056                        cn : [
14057                            {
14058                                tag : 'span',
14059                                html : this.fieldLabel
14060                            },
14061                            indicator
14062                        ]
14063
14064                     },
14065                     combobox
14066
14067                 ];
14068
14069             }
14070
14071         } else {
14072             
14073 //                Roo.log(" no label && no align");
14074                 cfg = combobox
14075                      
14076                 
14077         }
14078         
14079         var settings=this;
14080         ['xs','sm','md','lg'].map(function(size){
14081             if (settings[size]) {
14082                 cfg.cls += ' col-' + size + '-' + settings[size];
14083             }
14084         });
14085         
14086         return cfg;
14087         
14088     },
14089     
14090     
14091     
14092     // private
14093     onResize : function(w, h){
14094 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14095 //        if(typeof w == 'number'){
14096 //            var x = w - this.trigger.getWidth();
14097 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14098 //            this.trigger.setStyle('left', x+'px');
14099 //        }
14100     },
14101
14102     // private
14103     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14104
14105     // private
14106     getResizeEl : function(){
14107         return this.inputEl();
14108     },
14109
14110     // private
14111     getPositionEl : function(){
14112         return this.inputEl();
14113     },
14114
14115     // private
14116     alignErrorIcon : function(){
14117         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14118     },
14119
14120     // private
14121     initEvents : function(){
14122         
14123         this.createList();
14124         
14125         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14126         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14127         if(!this.multiple && this.showToggleBtn){
14128             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14129             if(this.hideTrigger){
14130                 this.trigger.setDisplayed(false);
14131             }
14132             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14133         }
14134         
14135         if(this.multiple){
14136             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14137         }
14138         
14139         if(this.removable && !this.editable && !this.tickable){
14140             var close = this.closeTriggerEl();
14141             
14142             if(close){
14143                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14144                 close.on('click', this.removeBtnClick, this, close);
14145             }
14146         }
14147         
14148         //this.trigger.addClassOnOver('x-form-trigger-over');
14149         //this.trigger.addClassOnClick('x-form-trigger-click');
14150         
14151         //if(!this.width){
14152         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14153         //}
14154     },
14155     
14156     closeTriggerEl : function()
14157     {
14158         var close = this.el.select('.roo-combo-removable-btn', true).first();
14159         return close ? close : false;
14160     },
14161     
14162     removeBtnClick : function(e, h, el)
14163     {
14164         e.preventDefault();
14165         
14166         if(this.fireEvent("remove", this) !== false){
14167             this.reset();
14168             this.fireEvent("afterremove", this)
14169         }
14170     },
14171     
14172     createList : function()
14173     {
14174         this.list = Roo.get(document.body).createChild({
14175             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14176             cls: 'typeahead typeahead-long dropdown-menu shadow',
14177             style: 'display:none'
14178         });
14179         
14180         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14181         
14182     },
14183
14184     // private
14185     initTrigger : function(){
14186        
14187     },
14188
14189     // private
14190     onDestroy : function(){
14191         if(this.trigger){
14192             this.trigger.removeAllListeners();
14193           //  this.trigger.remove();
14194         }
14195         //if(this.wrap){
14196         //    this.wrap.remove();
14197         //}
14198         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14199     },
14200
14201     // private
14202     onFocus : function(){
14203         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14204         /*
14205         if(!this.mimicing){
14206             this.wrap.addClass('x-trigger-wrap-focus');
14207             this.mimicing = true;
14208             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14209             if(this.monitorTab){
14210                 this.el.on("keydown", this.checkTab, this);
14211             }
14212         }
14213         */
14214     },
14215
14216     // private
14217     checkTab : function(e){
14218         if(e.getKey() == e.TAB){
14219             this.triggerBlur();
14220         }
14221     },
14222
14223     // private
14224     onBlur : function(){
14225         // do nothing
14226     },
14227
14228     // private
14229     mimicBlur : function(e, t){
14230         /*
14231         if(!this.wrap.contains(t) && this.validateBlur()){
14232             this.triggerBlur();
14233         }
14234         */
14235     },
14236
14237     // private
14238     triggerBlur : function(){
14239         this.mimicing = false;
14240         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14241         if(this.monitorTab){
14242             this.el.un("keydown", this.checkTab, this);
14243         }
14244         //this.wrap.removeClass('x-trigger-wrap-focus');
14245         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14246     },
14247
14248     // private
14249     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14250     validateBlur : function(e, t){
14251         return true;
14252     },
14253
14254     // private
14255     onDisable : function(){
14256         this.inputEl().dom.disabled = true;
14257         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14258         //if(this.wrap){
14259         //    this.wrap.addClass('x-item-disabled');
14260         //}
14261     },
14262
14263     // private
14264     onEnable : function(){
14265         this.inputEl().dom.disabled = false;
14266         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14267         //if(this.wrap){
14268         //    this.el.removeClass('x-item-disabled');
14269         //}
14270     },
14271
14272     // private
14273     onShow : function(){
14274         var ae = this.getActionEl();
14275         
14276         if(ae){
14277             ae.dom.style.display = '';
14278             ae.dom.style.visibility = 'visible';
14279         }
14280     },
14281
14282     // private
14283     
14284     onHide : function(){
14285         var ae = this.getActionEl();
14286         ae.dom.style.display = 'none';
14287     },
14288
14289     /**
14290      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14291      * by an implementing function.
14292      * @method
14293      * @param {EventObject} e
14294      */
14295     onTriggerClick : Roo.emptyFn
14296 });
14297  
14298 /*
14299 * Licence: LGPL
14300 */
14301
14302 /**
14303  * @class Roo.bootstrap.form.CardUploader
14304  * @extends Roo.bootstrap.Button
14305  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14306  * @cfg {Number} errorTimeout default 3000
14307  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14308  * @cfg {Array}  html The button text.
14309
14310  *
14311  * @constructor
14312  * Create a new CardUploader
14313  * @param {Object} config The config object
14314  */
14315
14316 Roo.bootstrap.form.CardUploader = function(config){
14317     
14318  
14319     
14320     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14321     
14322     
14323     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14324         return r.data.id
14325      });
14326     
14327      this.addEvents({
14328          // raw events
14329         /**
14330          * @event preview
14331          * When a image is clicked on - and needs to display a slideshow or similar..
14332          * @param {Roo.bootstrap.Card} this
14333          * @param {Object} The image information data 
14334          *
14335          */
14336         'preview' : true,
14337          /**
14338          * @event download
14339          * When a the download link is clicked
14340          * @param {Roo.bootstrap.Card} this
14341          * @param {Object} The image information data  contains 
14342          */
14343         'download' : true
14344         
14345     });
14346 };
14347  
14348 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14349     
14350      
14351     errorTimeout : 3000,
14352      
14353     images : false,
14354    
14355     fileCollection : false,
14356     allowBlank : true,
14357     
14358     getAutoCreate : function()
14359     {
14360         
14361         var cfg =  {
14362             cls :'form-group' ,
14363             cn : [
14364                
14365                 {
14366                     tag: 'label',
14367                    //cls : 'input-group-addon',
14368                     html : this.fieldLabel
14369
14370                 },
14371
14372                 {
14373                     tag: 'input',
14374                     type : 'hidden',
14375                     name : this.name,
14376                     value : this.value,
14377                     cls : 'd-none  form-control'
14378                 },
14379                 
14380                 {
14381                     tag: 'input',
14382                     multiple : 'multiple',
14383                     type : 'file',
14384                     cls : 'd-none  roo-card-upload-selector'
14385                 },
14386                 
14387                 {
14388                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14389                 },
14390                 {
14391                     cls : 'card-columns roo-card-uploader-container'
14392                 }
14393
14394             ]
14395         };
14396            
14397          
14398         return cfg;
14399     },
14400     
14401     getChildContainer : function() /// what children are added to.
14402     {
14403         return this.containerEl;
14404     },
14405    
14406     getButtonContainer : function() /// what children are added to.
14407     {
14408         return this.el.select(".roo-card-uploader-button-container").first();
14409     },
14410    
14411     initEvents : function()
14412     {
14413         
14414         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14415         
14416         var t = this;
14417         this.addxtype({
14418             xns: Roo.bootstrap,
14419
14420             xtype : 'Button',
14421             container_method : 'getButtonContainer' ,            
14422             html :  this.html, // fix changable?
14423             cls : 'w-100 ',
14424             listeners : {
14425                 'click' : function(btn, e) {
14426                     t.onClick(e);
14427                 }
14428             }
14429         });
14430         
14431         
14432         
14433         
14434         this.urlAPI = (window.createObjectURL && window) || 
14435                                 (window.URL && URL.revokeObjectURL && URL) || 
14436                                 (window.webkitURL && webkitURL);
14437                         
14438          
14439          
14440          
14441         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14442         
14443         this.selectorEl.on('change', this.onFileSelected, this);
14444         if (this.images) {
14445             var t = this;
14446             this.images.forEach(function(img) {
14447                 t.addCard(img)
14448             });
14449             this.images = false;
14450         }
14451         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14452          
14453        
14454     },
14455     
14456    
14457     onClick : function(e)
14458     {
14459         e.preventDefault();
14460          
14461         this.selectorEl.dom.click();
14462          
14463     },
14464     
14465     onFileSelected : function(e)
14466     {
14467         e.preventDefault();
14468         
14469         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14470             return;
14471         }
14472         
14473         Roo.each(this.selectorEl.dom.files, function(file){    
14474             this.addFile(file);
14475         }, this);
14476          
14477     },
14478     
14479       
14480     
14481       
14482     
14483     addFile : function(file)
14484     {
14485            
14486         if(typeof(file) === 'string'){
14487             throw "Add file by name?"; // should not happen
14488             return;
14489         }
14490         
14491         if(!file || !this.urlAPI){
14492             return;
14493         }
14494         
14495         // file;
14496         // file.type;
14497         
14498         var _this = this;
14499         
14500         
14501         var url = _this.urlAPI.createObjectURL( file);
14502            
14503         this.addCard({
14504             id : Roo.bootstrap.form.CardUploader.ID--,
14505             is_uploaded : false,
14506             src : url,
14507             srcfile : file,
14508             title : file.name,
14509             mimetype : file.type,
14510             preview : false,
14511             is_deleted : 0
14512         });
14513         
14514     },
14515     
14516     /**
14517      * addCard - add an Attachment to the uploader
14518      * @param data - the data about the image to upload
14519      *
14520      * {
14521           id : 123
14522           title : "Title of file",
14523           is_uploaded : false,
14524           src : "http://.....",
14525           srcfile : { the File upload object },
14526           mimetype : file.type,
14527           preview : false,
14528           is_deleted : 0
14529           .. any other data...
14530         }
14531      *
14532      * 
14533     */
14534     
14535     addCard : function (data)
14536     {
14537         // hidden input element?
14538         // if the file is not an image...
14539         //then we need to use something other that and header_image
14540         var t = this;
14541         //   remove.....
14542         var footer = [
14543             {
14544                 xns : Roo.bootstrap,
14545                 xtype : 'CardFooter',
14546                  items: [
14547                     {
14548                         xns : Roo.bootstrap,
14549                         xtype : 'Element',
14550                         cls : 'd-flex',
14551                         items : [
14552                             
14553                             {
14554                                 xns : Roo.bootstrap,
14555                                 xtype : 'Button',
14556                                 html : String.format("<small>{0}</small>", data.title),
14557                                 cls : 'col-10 text-left',
14558                                 size: 'sm',
14559                                 weight: 'link',
14560                                 fa : 'download',
14561                                 listeners : {
14562                                     click : function() {
14563                                      
14564                                         t.fireEvent( "download", t, data );
14565                                     }
14566                                 }
14567                             },
14568                           
14569                             {
14570                                 xns : Roo.bootstrap,
14571                                 xtype : 'Button',
14572                                 style: 'max-height: 28px; ',
14573                                 size : 'sm',
14574                                 weight: 'danger',
14575                                 cls : 'col-2',
14576                                 fa : 'times',
14577                                 listeners : {
14578                                     click : function() {
14579                                         t.removeCard(data.id)
14580                                     }
14581                                 }
14582                             }
14583                         ]
14584                     }
14585                     
14586                 ] 
14587             }
14588             
14589         ];
14590         
14591         var cn = this.addxtype(
14592             {
14593                  
14594                 xns : Roo.bootstrap,
14595                 xtype : 'Card',
14596                 closeable : true,
14597                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14598                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14599                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14600                 data : data,
14601                 html : false,
14602                  
14603                 items : footer,
14604                 initEvents : function() {
14605                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14606                     var card = this;
14607                     this.imgEl = this.el.select('.card-img-top').first();
14608                     if (this.imgEl) {
14609                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14610                         this.imgEl.set({ 'pointer' : 'cursor' });
14611                                   
14612                     }
14613                     this.getCardFooter().addClass('p-1');
14614                     
14615                   
14616                 }
14617                 
14618             }
14619         );
14620         // dont' really need ot update items.
14621         // this.items.push(cn);
14622         this.fileCollection.add(cn);
14623         
14624         if (!data.srcfile) {
14625             this.updateInput();
14626             return;
14627         }
14628             
14629         var _t = this;
14630         var reader = new FileReader();
14631         reader.addEventListener("load", function() {  
14632             data.srcdata =  reader.result;
14633             _t.updateInput();
14634         });
14635         reader.readAsDataURL(data.srcfile);
14636         
14637         
14638         
14639     },
14640     removeCard : function(id)
14641     {
14642         
14643         var card  = this.fileCollection.get(id);
14644         card.data.is_deleted = 1;
14645         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14646         //this.fileCollection.remove(card);
14647         //this.items = this.items.filter(function(e) { return e != card });
14648         // dont' really need ot update items.
14649         card.el.dom.parentNode.removeChild(card.el.dom);
14650         this.updateInput();
14651
14652         
14653     },
14654     reset: function()
14655     {
14656         this.fileCollection.each(function(card) {
14657             if (card.el.dom && card.el.dom.parentNode) {
14658                 card.el.dom.parentNode.removeChild(card.el.dom);
14659             }
14660         });
14661         this.fileCollection.clear();
14662         this.updateInput();
14663     },
14664     
14665     updateInput : function()
14666     {
14667          var data = [];
14668         this.fileCollection.each(function(e) {
14669             data.push(e.data);
14670             
14671         });
14672         this.inputEl().dom.value = JSON.stringify(data);
14673         
14674         
14675         
14676     }
14677     
14678     
14679 });
14680
14681
14682 Roo.bootstrap.form.CardUploader.ID = -1;/*
14683  * Based on:
14684  * Ext JS Library 1.1.1
14685  * Copyright(c) 2006-2007, Ext JS, LLC.
14686  *
14687  * Originally Released Under LGPL - original licence link has changed is not relivant.
14688  *
14689  * Fork - LGPL
14690  * <script type="text/javascript">
14691  */
14692
14693
14694 /**
14695  * @class Roo.data.SortTypes
14696  * @static
14697  * Defines the default sorting (casting?) comparison functions used when sorting data.
14698  */
14699 Roo.data.SortTypes = {
14700     /**
14701      * Default sort that does nothing
14702      * @param {Mixed} s The value being converted
14703      * @return {Mixed} The comparison value
14704      */
14705     none : function(s){
14706         return s;
14707     },
14708     
14709     /**
14710      * The regular expression used to strip tags
14711      * @type {RegExp}
14712      * @property
14713      */
14714     stripTagsRE : /<\/?[^>]+>/gi,
14715     
14716     /**
14717      * Strips all HTML tags to sort on text only
14718      * @param {Mixed} s The value being converted
14719      * @return {String} The comparison value
14720      */
14721     asText : function(s){
14722         return String(s).replace(this.stripTagsRE, "");
14723     },
14724     
14725     /**
14726      * Strips all HTML tags to sort on text only - Case insensitive
14727      * @param {Mixed} s The value being converted
14728      * @return {String} The comparison value
14729      */
14730     asUCText : function(s){
14731         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14732     },
14733     
14734     /**
14735      * Case insensitive string
14736      * @param {Mixed} s The value being converted
14737      * @return {String} The comparison value
14738      */
14739     asUCString : function(s) {
14740         return String(s).toUpperCase();
14741     },
14742     
14743     /**
14744      * Date sorting
14745      * @param {Mixed} s The value being converted
14746      * @return {Number} The comparison value
14747      */
14748     asDate : function(s) {
14749         if(!s){
14750             return 0;
14751         }
14752         if(s instanceof Date){
14753             return s.getTime();
14754         }
14755         return Date.parse(String(s));
14756     },
14757     
14758     /**
14759      * Float sorting
14760      * @param {Mixed} s The value being converted
14761      * @return {Float} The comparison value
14762      */
14763     asFloat : function(s) {
14764         var val = parseFloat(String(s).replace(/,/g, ""));
14765         if(isNaN(val)) {
14766             val = 0;
14767         }
14768         return val;
14769     },
14770     
14771     /**
14772      * Integer sorting
14773      * @param {Mixed} s The value being converted
14774      * @return {Number} The comparison value
14775      */
14776     asInt : function(s) {
14777         var val = parseInt(String(s).replace(/,/g, ""));
14778         if(isNaN(val)) {
14779             val = 0;
14780         }
14781         return val;
14782     }
14783 };/*
14784  * Based on:
14785  * Ext JS Library 1.1.1
14786  * Copyright(c) 2006-2007, Ext JS, LLC.
14787  *
14788  * Originally Released Under LGPL - original licence link has changed is not relivant.
14789  *
14790  * Fork - LGPL
14791  * <script type="text/javascript">
14792  */
14793
14794 /**
14795 * @class Roo.data.Record
14796  * Instances of this class encapsulate both record <em>definition</em> information, and record
14797  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14798  * to access Records cached in an {@link Roo.data.Store} object.<br>
14799  * <p>
14800  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14801  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14802  * objects.<br>
14803  * <p>
14804  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14805  * @constructor
14806  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14807  * {@link #create}. The parameters are the same.
14808  * @param {Array} data An associative Array of data values keyed by the field name.
14809  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14810  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14811  * not specified an integer id is generated.
14812  */
14813 Roo.data.Record = function(data, id){
14814     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14815     this.data = data;
14816 };
14817
14818 /**
14819  * Generate a constructor for a specific record layout.
14820  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14821  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14822  * Each field definition object may contain the following properties: <ul>
14823  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
14824  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14825  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14826  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14827  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14828  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14829  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14830  * this may be omitted.</p></li>
14831  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14832  * <ul><li>auto (Default, implies no conversion)</li>
14833  * <li>string</li>
14834  * <li>int</li>
14835  * <li>float</li>
14836  * <li>boolean</li>
14837  * <li>date</li></ul></p></li>
14838  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14839  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14840  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14841  * by the Reader into an object that will be stored in the Record. It is passed the
14842  * following parameters:<ul>
14843  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14844  * </ul></p></li>
14845  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14846  * </ul>
14847  * <br>usage:<br><pre><code>
14848 var TopicRecord = Roo.data.Record.create(
14849     {name: 'title', mapping: 'topic_title'},
14850     {name: 'author', mapping: 'username'},
14851     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14852     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14853     {name: 'lastPoster', mapping: 'user2'},
14854     {name: 'excerpt', mapping: 'post_text'}
14855 );
14856
14857 var myNewRecord = new TopicRecord({
14858     title: 'Do my job please',
14859     author: 'noobie',
14860     totalPosts: 1,
14861     lastPost: new Date(),
14862     lastPoster: 'Animal',
14863     excerpt: 'No way dude!'
14864 });
14865 myStore.add(myNewRecord);
14866 </code></pre>
14867  * @method create
14868  * @static
14869  */
14870 Roo.data.Record.create = function(o){
14871     var f = function(){
14872         f.superclass.constructor.apply(this, arguments);
14873     };
14874     Roo.extend(f, Roo.data.Record);
14875     var p = f.prototype;
14876     p.fields = new Roo.util.MixedCollection(false, function(field){
14877         return field.name;
14878     });
14879     for(var i = 0, len = o.length; i < len; i++){
14880         p.fields.add(new Roo.data.Field(o[i]));
14881     }
14882     f.getField = function(name){
14883         return p.fields.get(name);  
14884     };
14885     return f;
14886 };
14887
14888 Roo.data.Record.AUTO_ID = 1000;
14889 Roo.data.Record.EDIT = 'edit';
14890 Roo.data.Record.REJECT = 'reject';
14891 Roo.data.Record.COMMIT = 'commit';
14892
14893 Roo.data.Record.prototype = {
14894     /**
14895      * Readonly flag - true if this record has been modified.
14896      * @type Boolean
14897      */
14898     dirty : false,
14899     editing : false,
14900     error: null,
14901     modified: null,
14902
14903     // private
14904     join : function(store){
14905         this.store = store;
14906     },
14907
14908     /**
14909      * Set the named field to the specified value.
14910      * @param {String} name The name of the field to set.
14911      * @param {Object} value The value to set the field to.
14912      */
14913     set : function(name, value){
14914         if(this.data[name] == value){
14915             return;
14916         }
14917         this.dirty = true;
14918         if(!this.modified){
14919             this.modified = {};
14920         }
14921         if(typeof this.modified[name] == 'undefined'){
14922             this.modified[name] = this.data[name];
14923         }
14924         this.data[name] = value;
14925         if(!this.editing && this.store){
14926             this.store.afterEdit(this);
14927         }       
14928     },
14929
14930     /**
14931      * Get the value of the named field.
14932      * @param {String} name The name of the field to get the value of.
14933      * @return {Object} The value of the field.
14934      */
14935     get : function(name){
14936         return this.data[name]; 
14937     },
14938
14939     // private
14940     beginEdit : function(){
14941         this.editing = true;
14942         this.modified = {}; 
14943     },
14944
14945     // private
14946     cancelEdit : function(){
14947         this.editing = false;
14948         delete this.modified;
14949     },
14950
14951     // private
14952     endEdit : function(){
14953         this.editing = false;
14954         if(this.dirty && this.store){
14955             this.store.afterEdit(this);
14956         }
14957     },
14958
14959     /**
14960      * Usually called by the {@link Roo.data.Store} which owns the Record.
14961      * Rejects all changes made to the Record since either creation, or the last commit operation.
14962      * Modified fields are reverted to their original values.
14963      * <p>
14964      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14965      * of reject operations.
14966      */
14967     reject : function(){
14968         var m = this.modified;
14969         for(var n in m){
14970             if(typeof m[n] != "function"){
14971                 this.data[n] = m[n];
14972             }
14973         }
14974         this.dirty = false;
14975         delete this.modified;
14976         this.editing = false;
14977         if(this.store){
14978             this.store.afterReject(this);
14979         }
14980     },
14981
14982     /**
14983      * Usually called by the {@link Roo.data.Store} which owns the Record.
14984      * Commits all changes made to the Record since either creation, or the last commit operation.
14985      * <p>
14986      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14987      * of commit operations.
14988      */
14989     commit : function(){
14990         this.dirty = false;
14991         delete this.modified;
14992         this.editing = false;
14993         if(this.store){
14994             this.store.afterCommit(this);
14995         }
14996     },
14997
14998     // private
14999     hasError : function(){
15000         return this.error != null;
15001     },
15002
15003     // private
15004     clearError : function(){
15005         this.error = null;
15006     },
15007
15008     /**
15009      * Creates a copy of this record.
15010      * @param {String} id (optional) A new record id if you don't want to use this record's id
15011      * @return {Record}
15012      */
15013     copy : function(newId) {
15014         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15015     }
15016 };/*
15017  * Based on:
15018  * Ext JS Library 1.1.1
15019  * Copyright(c) 2006-2007, Ext JS, LLC.
15020  *
15021  * Originally Released Under LGPL - original licence link has changed is not relivant.
15022  *
15023  * Fork - LGPL
15024  * <script type="text/javascript">
15025  */
15026
15027
15028
15029 /**
15030  * @class Roo.data.Store
15031  * @extends Roo.util.Observable
15032  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15033  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15034  * <p>
15035  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
15036  * has no knowledge of the format of the data returned by the Proxy.<br>
15037  * <p>
15038  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15039  * instances from the data object. These records are cached and made available through accessor functions.
15040  * @constructor
15041  * Creates a new Store.
15042  * @param {Object} config A config object containing the objects needed for the Store to access data,
15043  * and read the data into Records.
15044  */
15045 Roo.data.Store = function(config){
15046     this.data = new Roo.util.MixedCollection(false);
15047     this.data.getKey = function(o){
15048         return o.id;
15049     };
15050     this.baseParams = {};
15051     // private
15052     this.paramNames = {
15053         "start" : "start",
15054         "limit" : "limit",
15055         "sort" : "sort",
15056         "dir" : "dir",
15057         "multisort" : "_multisort"
15058     };
15059
15060     if(config && config.data){
15061         this.inlineData = config.data;
15062         delete config.data;
15063     }
15064
15065     Roo.apply(this, config);
15066     
15067     if(this.reader){ // reader passed
15068         this.reader = Roo.factory(this.reader, Roo.data);
15069         this.reader.xmodule = this.xmodule || false;
15070         if(!this.recordType){
15071             this.recordType = this.reader.recordType;
15072         }
15073         if(this.reader.onMetaChange){
15074             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15075         }
15076     }
15077
15078     if(this.recordType){
15079         this.fields = this.recordType.prototype.fields;
15080     }
15081     this.modified = [];
15082
15083     this.addEvents({
15084         /**
15085          * @event datachanged
15086          * Fires when the data cache has changed, and a widget which is using this Store
15087          * as a Record cache should refresh its view.
15088          * @param {Store} this
15089          */
15090         datachanged : true,
15091         /**
15092          * @event metachange
15093          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15094          * @param {Store} this
15095          * @param {Object} meta The JSON metadata
15096          */
15097         metachange : true,
15098         /**
15099          * @event add
15100          * Fires when Records have been added to the Store
15101          * @param {Store} this
15102          * @param {Roo.data.Record[]} records The array of Records added
15103          * @param {Number} index The index at which the record(s) were added
15104          */
15105         add : true,
15106         /**
15107          * @event remove
15108          * Fires when a Record has been removed from the Store
15109          * @param {Store} this
15110          * @param {Roo.data.Record} record The Record that was removed
15111          * @param {Number} index The index at which the record was removed
15112          */
15113         remove : true,
15114         /**
15115          * @event update
15116          * Fires when a Record has been updated
15117          * @param {Store} this
15118          * @param {Roo.data.Record} record The Record that was updated
15119          * @param {String} operation The update operation being performed.  Value may be one of:
15120          * <pre><code>
15121  Roo.data.Record.EDIT
15122  Roo.data.Record.REJECT
15123  Roo.data.Record.COMMIT
15124          * </code></pre>
15125          */
15126         update : true,
15127         /**
15128          * @event clear
15129          * Fires when the data cache has been cleared.
15130          * @param {Store} this
15131          */
15132         clear : true,
15133         /**
15134          * @event beforeload
15135          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15136          * the load action will be canceled.
15137          * @param {Store} this
15138          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15139          */
15140         beforeload : true,
15141         /**
15142          * @event beforeloadadd
15143          * Fires after a new set of Records has been loaded.
15144          * @param {Store} this
15145          * @param {Roo.data.Record[]} records The Records that were loaded
15146          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15147          */
15148         beforeloadadd : true,
15149         /**
15150          * @event load
15151          * Fires after a new set of Records has been loaded, before they are added to the store.
15152          * @param {Store} this
15153          * @param {Roo.data.Record[]} records The Records that were loaded
15154          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15155          * @params {Object} return from reader
15156          */
15157         load : true,
15158         /**
15159          * @event loadexception
15160          * Fires if an exception occurs in the Proxy during loading.
15161          * Called with the signature of the Proxy's "loadexception" event.
15162          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15163          * 
15164          * @param {Proxy} 
15165          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15166          * @param {Object} load options 
15167          * @param {Object} jsonData from your request (normally this contains the Exception)
15168          */
15169         loadexception : true
15170     });
15171     
15172     if(this.proxy){
15173         this.proxy = Roo.factory(this.proxy, Roo.data);
15174         this.proxy.xmodule = this.xmodule || false;
15175         this.relayEvents(this.proxy,  ["loadexception"]);
15176     }
15177     this.sortToggle = {};
15178     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15179
15180     Roo.data.Store.superclass.constructor.call(this);
15181
15182     if(this.inlineData){
15183         this.loadData(this.inlineData);
15184         delete this.inlineData;
15185     }
15186 };
15187
15188 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15189      /**
15190     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15191     * without a remote query - used by combo/forms at present.
15192     */
15193     
15194     /**
15195     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15196     */
15197     /**
15198     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15199     */
15200     /**
15201     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15202     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15203     */
15204     /**
15205     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15206     * on any HTTP request
15207     */
15208     /**
15209     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15210     */
15211     /**
15212     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15213     */
15214     multiSort: false,
15215     /**
15216     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15217     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15218     */
15219     remoteSort : false,
15220
15221     /**
15222     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15223      * loaded or when a record is removed. (defaults to false).
15224     */
15225     pruneModifiedRecords : false,
15226
15227     // private
15228     lastOptions : null,
15229
15230     /**
15231      * Add Records to the Store and fires the add event.
15232      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15233      */
15234     add : function(records){
15235         records = [].concat(records);
15236         for(var i = 0, len = records.length; i < len; i++){
15237             records[i].join(this);
15238         }
15239         var index = this.data.length;
15240         this.data.addAll(records);
15241         this.fireEvent("add", this, records, index);
15242     },
15243
15244     /**
15245      * Remove a Record from the Store and fires the remove event.
15246      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15247      */
15248     remove : function(record){
15249         var index = this.data.indexOf(record);
15250         this.data.removeAt(index);
15251  
15252         if(this.pruneModifiedRecords){
15253             this.modified.remove(record);
15254         }
15255         this.fireEvent("remove", this, record, index);
15256     },
15257
15258     /**
15259      * Remove all Records from the Store and fires the clear event.
15260      */
15261     removeAll : function(){
15262         this.data.clear();
15263         if(this.pruneModifiedRecords){
15264             this.modified = [];
15265         }
15266         this.fireEvent("clear", this);
15267     },
15268
15269     /**
15270      * Inserts Records to the Store at the given index and fires the add event.
15271      * @param {Number} index The start index at which to insert the passed Records.
15272      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15273      */
15274     insert : function(index, records){
15275         records = [].concat(records);
15276         for(var i = 0, len = records.length; i < len; i++){
15277             this.data.insert(index, records[i]);
15278             records[i].join(this);
15279         }
15280         this.fireEvent("add", this, records, index);
15281     },
15282
15283     /**
15284      * Get the index within the cache of the passed Record.
15285      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15286      * @return {Number} The index of the passed Record. Returns -1 if not found.
15287      */
15288     indexOf : function(record){
15289         return this.data.indexOf(record);
15290     },
15291
15292     /**
15293      * Get the index within the cache of the Record with the passed id.
15294      * @param {String} id The id of the Record to find.
15295      * @return {Number} The index of the Record. Returns -1 if not found.
15296      */
15297     indexOfId : function(id){
15298         return this.data.indexOfKey(id);
15299     },
15300
15301     /**
15302      * Get the Record with the specified id.
15303      * @param {String} id The id of the Record to find.
15304      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15305      */
15306     getById : function(id){
15307         return this.data.key(id);
15308     },
15309
15310     /**
15311      * Get the Record at the specified index.
15312      * @param {Number} index The index of the Record to find.
15313      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15314      */
15315     getAt : function(index){
15316         return this.data.itemAt(index);
15317     },
15318
15319     /**
15320      * Returns a range of Records between specified indices.
15321      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15322      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15323      * @return {Roo.data.Record[]} An array of Records
15324      */
15325     getRange : function(start, end){
15326         return this.data.getRange(start, end);
15327     },
15328
15329     // private
15330     storeOptions : function(o){
15331         o = Roo.apply({}, o);
15332         delete o.callback;
15333         delete o.scope;
15334         this.lastOptions = o;
15335     },
15336
15337     /**
15338      * Loads the Record cache from the configured Proxy using the configured Reader.
15339      * <p>
15340      * If using remote paging, then the first load call must specify the <em>start</em>
15341      * and <em>limit</em> properties in the options.params property to establish the initial
15342      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15343      * <p>
15344      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15345      * and this call will return before the new data has been loaded. Perform any post-processing
15346      * in a callback function, or in a "load" event handler.</strong>
15347      * <p>
15348      * @param {Object} options An object containing properties which control loading options:<ul>
15349      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15350      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15351      * passed the following arguments:<ul>
15352      * <li>r : Roo.data.Record[]</li>
15353      * <li>options: Options object from the load call</li>
15354      * <li>success: Boolean success indicator</li></ul></li>
15355      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15356      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15357      * </ul>
15358      */
15359     load : function(options){
15360         options = options || {};
15361         if(this.fireEvent("beforeload", this, options) !== false){
15362             this.storeOptions(options);
15363             var p = Roo.apply(options.params || {}, this.baseParams);
15364             // if meta was not loaded from remote source.. try requesting it.
15365             if (!this.reader.metaFromRemote) {
15366                 p._requestMeta = 1;
15367             }
15368             if(this.sortInfo && this.remoteSort){
15369                 var pn = this.paramNames;
15370                 p[pn["sort"]] = this.sortInfo.field;
15371                 p[pn["dir"]] = this.sortInfo.direction;
15372             }
15373             if (this.multiSort) {
15374                 var pn = this.paramNames;
15375                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15376             }
15377             
15378             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15379         }
15380     },
15381
15382     /**
15383      * Reloads the Record cache from the configured Proxy using the configured Reader and
15384      * the options from the last load operation performed.
15385      * @param {Object} options (optional) An object containing properties which may override the options
15386      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15387      * the most recently used options are reused).
15388      */
15389     reload : function(options){
15390         this.load(Roo.applyIf(options||{}, this.lastOptions));
15391     },
15392
15393     // private
15394     // Called as a callback by the Reader during a load operation.
15395     loadRecords : function(o, options, success){
15396          
15397         if(!o){
15398             if(success !== false){
15399                 this.fireEvent("load", this, [], options, o);
15400             }
15401             if(options.callback){
15402                 options.callback.call(options.scope || this, [], options, false);
15403             }
15404             return;
15405         }
15406         // if data returned failure - throw an exception.
15407         if (o.success === false) {
15408             // show a message if no listener is registered.
15409             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15410                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15411             }
15412             // loadmask wil be hooked into this..
15413             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15414             return;
15415         }
15416         var r = o.records, t = o.totalRecords || r.length;
15417         
15418         this.fireEvent("beforeloadadd", this, r, options, o);
15419         
15420         if(!options || options.add !== true){
15421             if(this.pruneModifiedRecords){
15422                 this.modified = [];
15423             }
15424             for(var i = 0, len = r.length; i < len; i++){
15425                 r[i].join(this);
15426             }
15427             if(this.snapshot){
15428                 this.data = this.snapshot;
15429                 delete this.snapshot;
15430             }
15431             this.data.clear();
15432             this.data.addAll(r);
15433             this.totalLength = t;
15434             this.applySort();
15435             this.fireEvent("datachanged", this);
15436         }else{
15437             this.totalLength = Math.max(t, this.data.length+r.length);
15438             this.add(r);
15439         }
15440         
15441         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15442                 
15443             var e = new Roo.data.Record({});
15444
15445             e.set(this.parent.displayField, this.parent.emptyTitle);
15446             e.set(this.parent.valueField, '');
15447
15448             this.insert(0, e);
15449         }
15450             
15451         this.fireEvent("load", this, r, options, o);
15452         if(options.callback){
15453             options.callback.call(options.scope || this, r, options, true);
15454         }
15455     },
15456
15457
15458     /**
15459      * Loads data from a passed data block. A Reader which understands the format of the data
15460      * must have been configured in the constructor.
15461      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15462      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15463      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15464      */
15465     loadData : function(o, append){
15466         var r = this.reader.readRecords(o);
15467         this.loadRecords(r, {add: append}, true);
15468     },
15469     
15470      /**
15471      * using 'cn' the nested child reader read the child array into it's child stores.
15472      * @param {Object} rec The record with a 'children array
15473      */
15474     loadDataFromChildren : function(rec)
15475     {
15476         this.loadData(this.reader.toLoadData(rec));
15477     },
15478     
15479
15480     /**
15481      * Gets the number of cached records.
15482      * <p>
15483      * <em>If using paging, this may not be the total size of the dataset. If the data object
15484      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15485      * the data set size</em>
15486      */
15487     getCount : function(){
15488         return this.data.length || 0;
15489     },
15490
15491     /**
15492      * Gets the total number of records in the dataset as returned by the server.
15493      * <p>
15494      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15495      * the dataset size</em>
15496      */
15497     getTotalCount : function(){
15498         return this.totalLength || 0;
15499     },
15500
15501     /**
15502      * Returns the sort state of the Store as an object with two properties:
15503      * <pre><code>
15504  field {String} The name of the field by which the Records are sorted
15505  direction {String} The sort order, "ASC" or "DESC"
15506      * </code></pre>
15507      */
15508     getSortState : function(){
15509         return this.sortInfo;
15510     },
15511
15512     // private
15513     applySort : function(){
15514         if(this.sortInfo && !this.remoteSort){
15515             var s = this.sortInfo, f = s.field;
15516             var st = this.fields.get(f).sortType;
15517             var fn = function(r1, r2){
15518                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15519                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15520             };
15521             this.data.sort(s.direction, fn);
15522             if(this.snapshot && this.snapshot != this.data){
15523                 this.snapshot.sort(s.direction, fn);
15524             }
15525         }
15526     },
15527
15528     /**
15529      * Sets the default sort column and order to be used by the next load operation.
15530      * @param {String} fieldName The name of the field to sort by.
15531      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15532      */
15533     setDefaultSort : function(field, dir){
15534         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15535     },
15536
15537     /**
15538      * Sort the Records.
15539      * If remote sorting is used, the sort is performed on the server, and the cache is
15540      * reloaded. If local sorting is used, the cache is sorted internally.
15541      * @param {String} fieldName The name of the field to sort by.
15542      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15543      */
15544     sort : function(fieldName, dir){
15545         var f = this.fields.get(fieldName);
15546         if(!dir){
15547             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15548             
15549             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15550                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15551             }else{
15552                 dir = f.sortDir;
15553             }
15554         }
15555         this.sortToggle[f.name] = dir;
15556         this.sortInfo = {field: f.name, direction: dir};
15557         if(!this.remoteSort){
15558             this.applySort();
15559             this.fireEvent("datachanged", this);
15560         }else{
15561             this.load(this.lastOptions);
15562         }
15563     },
15564
15565     /**
15566      * Calls the specified function for each of the Records in the cache.
15567      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15568      * Returning <em>false</em> aborts and exits the iteration.
15569      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15570      */
15571     each : function(fn, scope){
15572         this.data.each(fn, scope);
15573     },
15574
15575     /**
15576      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15577      * (e.g., during paging).
15578      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15579      */
15580     getModifiedRecords : function(){
15581         return this.modified;
15582     },
15583
15584     // private
15585     createFilterFn : function(property, value, anyMatch){
15586         if(!value.exec){ // not a regex
15587             value = String(value);
15588             if(value.length == 0){
15589                 return false;
15590             }
15591             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15592         }
15593         return function(r){
15594             return value.test(r.data[property]);
15595         };
15596     },
15597
15598     /**
15599      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15600      * @param {String} property A field on your records
15601      * @param {Number} start The record index to start at (defaults to 0)
15602      * @param {Number} end The last record index to include (defaults to length - 1)
15603      * @return {Number} The sum
15604      */
15605     sum : function(property, start, end){
15606         var rs = this.data.items, v = 0;
15607         start = start || 0;
15608         end = (end || end === 0) ? end : rs.length-1;
15609
15610         for(var i = start; i <= end; i++){
15611             v += (rs[i].data[property] || 0);
15612         }
15613         return v;
15614     },
15615
15616     /**
15617      * Filter the records by a specified property.
15618      * @param {String} field A field on your records
15619      * @param {String/RegExp} value Either a string that the field
15620      * should start with or a RegExp to test against the field
15621      * @param {Boolean} anyMatch True to match any part not just the beginning
15622      */
15623     filter : function(property, value, anyMatch){
15624         var fn = this.createFilterFn(property, value, anyMatch);
15625         return fn ? this.filterBy(fn) : this.clearFilter();
15626     },
15627
15628     /**
15629      * Filter by a function. The specified function will be called with each
15630      * record in this data source. If the function returns true the record is included,
15631      * otherwise it is filtered.
15632      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15633      * @param {Object} scope (optional) The scope of the function (defaults to this)
15634      */
15635     filterBy : function(fn, scope){
15636         this.snapshot = this.snapshot || this.data;
15637         this.data = this.queryBy(fn, scope||this);
15638         this.fireEvent("datachanged", this);
15639     },
15640
15641     /**
15642      * Query the records by a specified property.
15643      * @param {String} field A field on your records
15644      * @param {String/RegExp} value Either a string that the field
15645      * should start with or a RegExp to test against the field
15646      * @param {Boolean} anyMatch True to match any part not just the beginning
15647      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15648      */
15649     query : function(property, value, anyMatch){
15650         var fn = this.createFilterFn(property, value, anyMatch);
15651         return fn ? this.queryBy(fn) : this.data.clone();
15652     },
15653
15654     /**
15655      * Query by a function. The specified function will be called with each
15656      * record in this data source. If the function returns true the record is included
15657      * in the results.
15658      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15659      * @param {Object} scope (optional) The scope of the function (defaults to this)
15660       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15661      **/
15662     queryBy : function(fn, scope){
15663         var data = this.snapshot || this.data;
15664         return data.filterBy(fn, scope||this);
15665     },
15666
15667     /**
15668      * Collects unique values for a particular dataIndex from this store.
15669      * @param {String} dataIndex The property to collect
15670      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15671      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15672      * @return {Array} An array of the unique values
15673      **/
15674     collect : function(dataIndex, allowNull, bypassFilter){
15675         var d = (bypassFilter === true && this.snapshot) ?
15676                 this.snapshot.items : this.data.items;
15677         var v, sv, r = [], l = {};
15678         for(var i = 0, len = d.length; i < len; i++){
15679             v = d[i].data[dataIndex];
15680             sv = String(v);
15681             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15682                 l[sv] = true;
15683                 r[r.length] = v;
15684             }
15685         }
15686         return r;
15687     },
15688
15689     /**
15690      * Revert to a view of the Record cache with no filtering applied.
15691      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15692      */
15693     clearFilter : function(suppressEvent){
15694         if(this.snapshot && this.snapshot != this.data){
15695             this.data = this.snapshot;
15696             delete this.snapshot;
15697             if(suppressEvent !== true){
15698                 this.fireEvent("datachanged", this);
15699             }
15700         }
15701     },
15702
15703     // private
15704     afterEdit : function(record){
15705         if(this.modified.indexOf(record) == -1){
15706             this.modified.push(record);
15707         }
15708         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15709     },
15710     
15711     // private
15712     afterReject : function(record){
15713         this.modified.remove(record);
15714         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15715     },
15716
15717     // private
15718     afterCommit : function(record){
15719         this.modified.remove(record);
15720         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15721     },
15722
15723     /**
15724      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15725      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15726      */
15727     commitChanges : function(){
15728         var m = this.modified.slice(0);
15729         this.modified = [];
15730         for(var i = 0, len = m.length; i < len; i++){
15731             m[i].commit();
15732         }
15733     },
15734
15735     /**
15736      * Cancel outstanding changes on all changed records.
15737      */
15738     rejectChanges : function(){
15739         var m = this.modified.slice(0);
15740         this.modified = [];
15741         for(var i = 0, len = m.length; i < len; i++){
15742             m[i].reject();
15743         }
15744     },
15745
15746     onMetaChange : function(meta, rtype, o){
15747         this.recordType = rtype;
15748         this.fields = rtype.prototype.fields;
15749         delete this.snapshot;
15750         this.sortInfo = meta.sortInfo || this.sortInfo;
15751         this.modified = [];
15752         this.fireEvent('metachange', this, this.reader.meta);
15753     },
15754     
15755     moveIndex : function(data, type)
15756     {
15757         var index = this.indexOf(data);
15758         
15759         var newIndex = index + type;
15760         
15761         this.remove(data);
15762         
15763         this.insert(newIndex, data);
15764         
15765     }
15766 });/*
15767  * Based on:
15768  * Ext JS Library 1.1.1
15769  * Copyright(c) 2006-2007, Ext JS, LLC.
15770  *
15771  * Originally Released Under LGPL - original licence link has changed is not relivant.
15772  *
15773  * Fork - LGPL
15774  * <script type="text/javascript">
15775  */
15776
15777 /**
15778  * @class Roo.data.SimpleStore
15779  * @extends Roo.data.Store
15780  * Small helper class to make creating Stores from Array data easier.
15781  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15782  * @cfg {Array} fields An array of field definition objects, or field name strings.
15783  * @cfg {Object} an existing reader (eg. copied from another store)
15784  * @cfg {Array} data The multi-dimensional array of data
15785  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15786  * @cfg {Roo.data.Reader} reader  [not-required] 
15787  * @constructor
15788  * @param {Object} config
15789  */
15790 Roo.data.SimpleStore = function(config)
15791 {
15792     Roo.data.SimpleStore.superclass.constructor.call(this, {
15793         isLocal : true,
15794         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15795                 id: config.id
15796             },
15797             Roo.data.Record.create(config.fields)
15798         ),
15799         proxy : new Roo.data.MemoryProxy(config.data)
15800     });
15801     this.load();
15802 };
15803 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15804  * Based on:
15805  * Ext JS Library 1.1.1
15806  * Copyright(c) 2006-2007, Ext JS, LLC.
15807  *
15808  * Originally Released Under LGPL - original licence link has changed is not relivant.
15809  *
15810  * Fork - LGPL
15811  * <script type="text/javascript">
15812  */
15813
15814 /**
15815 /**
15816  * @extends Roo.data.Store
15817  * @class Roo.data.JsonStore
15818  * Small helper class to make creating Stores for JSON data easier. <br/>
15819 <pre><code>
15820 var store = new Roo.data.JsonStore({
15821     url: 'get-images.php',
15822     root: 'images',
15823     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15824 });
15825 </code></pre>
15826  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15827  * JsonReader and HttpProxy (unless inline data is provided).</b>
15828  * @cfg {Array} fields An array of field definition objects, or field name strings.
15829  * @constructor
15830  * @param {Object} config
15831  */
15832 Roo.data.JsonStore = function(c){
15833     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15834         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15835         reader: new Roo.data.JsonReader(c, c.fields)
15836     }));
15837 };
15838 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15839  * Based on:
15840  * Ext JS Library 1.1.1
15841  * Copyright(c) 2006-2007, Ext JS, LLC.
15842  *
15843  * Originally Released Under LGPL - original licence link has changed is not relivant.
15844  *
15845  * Fork - LGPL
15846  * <script type="text/javascript">
15847  */
15848
15849  
15850 Roo.data.Field = function(config){
15851     if(typeof config == "string"){
15852         config = {name: config};
15853     }
15854     Roo.apply(this, config);
15855     
15856     if(!this.type){
15857         this.type = "auto";
15858     }
15859     
15860     var st = Roo.data.SortTypes;
15861     // named sortTypes are supported, here we look them up
15862     if(typeof this.sortType == "string"){
15863         this.sortType = st[this.sortType];
15864     }
15865     
15866     // set default sortType for strings and dates
15867     if(!this.sortType){
15868         switch(this.type){
15869             case "string":
15870                 this.sortType = st.asUCString;
15871                 break;
15872             case "date":
15873                 this.sortType = st.asDate;
15874                 break;
15875             default:
15876                 this.sortType = st.none;
15877         }
15878     }
15879
15880     // define once
15881     var stripRe = /[\$,%]/g;
15882
15883     // prebuilt conversion function for this field, instead of
15884     // switching every time we're reading a value
15885     if(!this.convert){
15886         var cv, dateFormat = this.dateFormat;
15887         switch(this.type){
15888             case "":
15889             case "auto":
15890             case undefined:
15891                 cv = function(v){ return v; };
15892                 break;
15893             case "string":
15894                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15895                 break;
15896             case "int":
15897                 cv = function(v){
15898                     return v !== undefined && v !== null && v !== '' ?
15899                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15900                     };
15901                 break;
15902             case "float":
15903                 cv = function(v){
15904                     return v !== undefined && v !== null && v !== '' ?
15905                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15906                     };
15907                 break;
15908             case "bool":
15909             case "boolean":
15910                 cv = function(v){ return v === true || v === "true" || v == 1; };
15911                 break;
15912             case "date":
15913                 cv = function(v){
15914                     if(!v){
15915                         return '';
15916                     }
15917                     if(v instanceof Date){
15918                         return v;
15919                     }
15920                     if(dateFormat){
15921                         if(dateFormat == "timestamp"){
15922                             return new Date(v*1000);
15923                         }
15924                         return Date.parseDate(v, dateFormat);
15925                     }
15926                     var parsed = Date.parse(v);
15927                     return parsed ? new Date(parsed) : null;
15928                 };
15929              break;
15930             
15931         }
15932         this.convert = cv;
15933     }
15934 };
15935
15936 Roo.data.Field.prototype = {
15937     dateFormat: null,
15938     defaultValue: "",
15939     mapping: null,
15940     sortType : null,
15941     sortDir : "ASC"
15942 };/*
15943  * Based on:
15944  * Ext JS Library 1.1.1
15945  * Copyright(c) 2006-2007, Ext JS, LLC.
15946  *
15947  * Originally Released Under LGPL - original licence link has changed is not relivant.
15948  *
15949  * Fork - LGPL
15950  * <script type="text/javascript">
15951  */
15952  
15953 // Base class for reading structured data from a data source.  This class is intended to be
15954 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15955
15956 /**
15957  * @class Roo.data.DataReader
15958  * @abstract
15959  * Base class for reading structured data from a data source.  This class is intended to be
15960  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15961  */
15962
15963 Roo.data.DataReader = function(meta, recordType){
15964     
15965     this.meta = meta;
15966     
15967     this.recordType = recordType instanceof Array ? 
15968         Roo.data.Record.create(recordType) : recordType;
15969 };
15970
15971 Roo.data.DataReader.prototype = {
15972     
15973     
15974     readerType : 'Data',
15975      /**
15976      * Create an empty record
15977      * @param {Object} data (optional) - overlay some values
15978      * @return {Roo.data.Record} record created.
15979      */
15980     newRow :  function(d) {
15981         var da =  {};
15982         this.recordType.prototype.fields.each(function(c) {
15983             switch( c.type) {
15984                 case 'int' : da[c.name] = 0; break;
15985                 case 'date' : da[c.name] = new Date(); break;
15986                 case 'float' : da[c.name] = 0.0; break;
15987                 case 'boolean' : da[c.name] = false; break;
15988                 default : da[c.name] = ""; break;
15989             }
15990             
15991         });
15992         return new this.recordType(Roo.apply(da, d));
15993     }
15994     
15995     
15996 };/*
15997  * Based on:
15998  * Ext JS Library 1.1.1
15999  * Copyright(c) 2006-2007, Ext JS, LLC.
16000  *
16001  * Originally Released Under LGPL - original licence link has changed is not relivant.
16002  *
16003  * Fork - LGPL
16004  * <script type="text/javascript">
16005  */
16006
16007 /**
16008  * @class Roo.data.DataProxy
16009  * @extends Roo.util.Observable
16010  * @abstract
16011  * This class is an abstract base class for implementations which provide retrieval of
16012  * unformatted data objects.<br>
16013  * <p>
16014  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16015  * (of the appropriate type which knows how to parse the data object) to provide a block of
16016  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16017  * <p>
16018  * Custom implementations must implement the load method as described in
16019  * {@link Roo.data.HttpProxy#load}.
16020  */
16021 Roo.data.DataProxy = function(){
16022     this.addEvents({
16023         /**
16024          * @event beforeload
16025          * Fires before a network request is made to retrieve a data object.
16026          * @param {Object} This DataProxy object.
16027          * @param {Object} params The params parameter to the load function.
16028          */
16029         beforeload : true,
16030         /**
16031          * @event load
16032          * Fires before the load method's callback is called.
16033          * @param {Object} This DataProxy object.
16034          * @param {Object} o The data object.
16035          * @param {Object} arg The callback argument object passed to the load function.
16036          */
16037         load : true,
16038         /**
16039          * @event loadexception
16040          * Fires if an Exception occurs during data retrieval.
16041          * @param {Object} This DataProxy object.
16042          * @param {Object} o The data object.
16043          * @param {Object} arg The callback argument object passed to the load function.
16044          * @param {Object} e The Exception.
16045          */
16046         loadexception : true
16047     });
16048     Roo.data.DataProxy.superclass.constructor.call(this);
16049 };
16050
16051 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16052
16053     /**
16054      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16055      */
16056 /*
16057  * Based on:
16058  * Ext JS Library 1.1.1
16059  * Copyright(c) 2006-2007, Ext JS, LLC.
16060  *
16061  * Originally Released Under LGPL - original licence link has changed is not relivant.
16062  *
16063  * Fork - LGPL
16064  * <script type="text/javascript">
16065  */
16066 /**
16067  * @class Roo.data.MemoryProxy
16068  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16069  * to the Reader when its load method is called.
16070  * @constructor
16071  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16072  */
16073 Roo.data.MemoryProxy = function(data){
16074     if (data.data) {
16075         data = data.data;
16076     }
16077     Roo.data.MemoryProxy.superclass.constructor.call(this);
16078     this.data = data;
16079 };
16080
16081 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16082     
16083     /**
16084      * Load data from the requested source (in this case an in-memory
16085      * data object passed to the constructor), read the data object into
16086      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16087      * process that block using the passed callback.
16088      * @param {Object} params This parameter is not used by the MemoryProxy class.
16089      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16090      * object into a block of Roo.data.Records.
16091      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16092      * The function must be passed <ul>
16093      * <li>The Record block object</li>
16094      * <li>The "arg" argument from the load function</li>
16095      * <li>A boolean success indicator</li>
16096      * </ul>
16097      * @param {Object} scope The scope in which to call the callback
16098      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16099      */
16100     load : function(params, reader, callback, scope, arg){
16101         params = params || {};
16102         var result;
16103         try {
16104             result = reader.readRecords(params.data ? params.data :this.data);
16105         }catch(e){
16106             this.fireEvent("loadexception", this, arg, null, e);
16107             callback.call(scope, null, arg, false);
16108             return;
16109         }
16110         callback.call(scope, result, arg, true);
16111     },
16112     
16113     // private
16114     update : function(params, records){
16115         
16116     }
16117 });/*
16118  * Based on:
16119  * Ext JS Library 1.1.1
16120  * Copyright(c) 2006-2007, Ext JS, LLC.
16121  *
16122  * Originally Released Under LGPL - original licence link has changed is not relivant.
16123  *
16124  * Fork - LGPL
16125  * <script type="text/javascript">
16126  */
16127 /**
16128  * @class Roo.data.HttpProxy
16129  * @extends Roo.data.DataProxy
16130  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16131  * configured to reference a certain URL.<br><br>
16132  * <p>
16133  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16134  * from which the running page was served.<br><br>
16135  * <p>
16136  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16137  * <p>
16138  * Be aware that to enable the browser to parse an XML document, the server must set
16139  * the Content-Type header in the HTTP response to "text/xml".
16140  * @constructor
16141  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16142  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16143  * will be used to make the request.
16144  */
16145 Roo.data.HttpProxy = function(conn){
16146     Roo.data.HttpProxy.superclass.constructor.call(this);
16147     // is conn a conn config or a real conn?
16148     this.conn = conn;
16149     this.useAjax = !conn || !conn.events;
16150   
16151 };
16152
16153 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16154     // thse are take from connection...
16155     
16156     /**
16157      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16158      */
16159     /**
16160      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16161      * extra parameters to each request made by this object. (defaults to undefined)
16162      */
16163     /**
16164      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16165      *  to each request made by this object. (defaults to undefined)
16166      */
16167     /**
16168      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
16169      */
16170     /**
16171      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16172      */
16173      /**
16174      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16175      * @type Boolean
16176      */
16177   
16178
16179     /**
16180      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16181      * @type Boolean
16182      */
16183     /**
16184      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16185      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16186      * a finer-grained basis than the DataProxy events.
16187      */
16188     getConnection : function(){
16189         return this.useAjax ? Roo.Ajax : this.conn;
16190     },
16191
16192     /**
16193      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16194      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16195      * process that block using the passed callback.
16196      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16197      * for the request to the remote server.
16198      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16199      * object into a block of Roo.data.Records.
16200      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16201      * The function must be passed <ul>
16202      * <li>The Record block object</li>
16203      * <li>The "arg" argument from the load function</li>
16204      * <li>A boolean success indicator</li>
16205      * </ul>
16206      * @param {Object} scope The scope in which to call the callback
16207      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16208      */
16209     load : function(params, reader, callback, scope, arg){
16210         if(this.fireEvent("beforeload", this, params) !== false){
16211             var  o = {
16212                 params : params || {},
16213                 request: {
16214                     callback : callback,
16215                     scope : scope,
16216                     arg : arg
16217                 },
16218                 reader: reader,
16219                 callback : this.loadResponse,
16220                 scope: this
16221             };
16222             if(this.useAjax){
16223                 Roo.applyIf(o, this.conn);
16224                 if(this.activeRequest){
16225                     Roo.Ajax.abort(this.activeRequest);
16226                 }
16227                 this.activeRequest = Roo.Ajax.request(o);
16228             }else{
16229                 this.conn.request(o);
16230             }
16231         }else{
16232             callback.call(scope||this, null, arg, false);
16233         }
16234     },
16235
16236     // private
16237     loadResponse : function(o, success, response){
16238         delete this.activeRequest;
16239         if(!success){
16240             this.fireEvent("loadexception", this, o, response);
16241             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16242             return;
16243         }
16244         var result;
16245         try {
16246             result = o.reader.read(response);
16247         }catch(e){
16248             o.success = false;
16249             o.raw = { errorMsg : response.responseText };
16250             this.fireEvent("loadexception", this, o, response, e);
16251             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16252             return;
16253         }
16254         
16255         this.fireEvent("load", this, o, o.request.arg);
16256         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16257     },
16258
16259     // private
16260     update : function(dataSet){
16261
16262     },
16263
16264     // private
16265     updateResponse : function(dataSet){
16266
16267     }
16268 });/*
16269  * Based on:
16270  * Ext JS Library 1.1.1
16271  * Copyright(c) 2006-2007, Ext JS, LLC.
16272  *
16273  * Originally Released Under LGPL - original licence link has changed is not relivant.
16274  *
16275  * Fork - LGPL
16276  * <script type="text/javascript">
16277  */
16278
16279 /**
16280  * @class Roo.data.ScriptTagProxy
16281  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16282  * other than the originating domain of the running page.<br><br>
16283  * <p>
16284  * <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
16285  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16286  * <p>
16287  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16288  * source code that is used as the source inside a &lt;script> tag.<br><br>
16289  * <p>
16290  * In order for the browser to process the returned data, the server must wrap the data object
16291  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16292  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16293  * depending on whether the callback name was passed:
16294  * <p>
16295  * <pre><code>
16296 boolean scriptTag = false;
16297 String cb = request.getParameter("callback");
16298 if (cb != null) {
16299     scriptTag = true;
16300     response.setContentType("text/javascript");
16301 } else {
16302     response.setContentType("application/x-json");
16303 }
16304 Writer out = response.getWriter();
16305 if (scriptTag) {
16306     out.write(cb + "(");
16307 }
16308 out.print(dataBlock.toJsonString());
16309 if (scriptTag) {
16310     out.write(");");
16311 }
16312 </pre></code>
16313  *
16314  * @constructor
16315  * @param {Object} config A configuration object.
16316  */
16317 Roo.data.ScriptTagProxy = function(config){
16318     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16319     Roo.apply(this, config);
16320     this.head = document.getElementsByTagName("head")[0];
16321 };
16322
16323 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16324
16325 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16326     /**
16327      * @cfg {String} url The URL from which to request the data object.
16328      */
16329     /**
16330      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16331      */
16332     timeout : 30000,
16333     /**
16334      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16335      * the server the name of the callback function set up by the load call to process the returned data object.
16336      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16337      * javascript output which calls this named function passing the data object as its only parameter.
16338      */
16339     callbackParam : "callback",
16340     /**
16341      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16342      * name to the request.
16343      */
16344     nocache : true,
16345
16346     /**
16347      * Load data from the configured URL, read the data object into
16348      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16349      * process that block using the passed callback.
16350      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16351      * for the request to the remote server.
16352      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16353      * object into a block of Roo.data.Records.
16354      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16355      * The function must be passed <ul>
16356      * <li>The Record block object</li>
16357      * <li>The "arg" argument from the load function</li>
16358      * <li>A boolean success indicator</li>
16359      * </ul>
16360      * @param {Object} scope The scope in which to call the callback
16361      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16362      */
16363     load : function(params, reader, callback, scope, arg){
16364         if(this.fireEvent("beforeload", this, params) !== false){
16365
16366             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16367
16368             var url = this.url;
16369             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16370             if(this.nocache){
16371                 url += "&_dc=" + (new Date().getTime());
16372             }
16373             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16374             var trans = {
16375                 id : transId,
16376                 cb : "stcCallback"+transId,
16377                 scriptId : "stcScript"+transId,
16378                 params : params,
16379                 arg : arg,
16380                 url : url,
16381                 callback : callback,
16382                 scope : scope,
16383                 reader : reader
16384             };
16385             var conn = this;
16386
16387             window[trans.cb] = function(o){
16388                 conn.handleResponse(o, trans);
16389             };
16390
16391             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16392
16393             if(this.autoAbort !== false){
16394                 this.abort();
16395             }
16396
16397             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16398
16399             var script = document.createElement("script");
16400             script.setAttribute("src", url);
16401             script.setAttribute("type", "text/javascript");
16402             script.setAttribute("id", trans.scriptId);
16403             this.head.appendChild(script);
16404
16405             this.trans = trans;
16406         }else{
16407             callback.call(scope||this, null, arg, false);
16408         }
16409     },
16410
16411     // private
16412     isLoading : function(){
16413         return this.trans ? true : false;
16414     },
16415
16416     /**
16417      * Abort the current server request.
16418      */
16419     abort : function(){
16420         if(this.isLoading()){
16421             this.destroyTrans(this.trans);
16422         }
16423     },
16424
16425     // private
16426     destroyTrans : function(trans, isLoaded){
16427         this.head.removeChild(document.getElementById(trans.scriptId));
16428         clearTimeout(trans.timeoutId);
16429         if(isLoaded){
16430             window[trans.cb] = undefined;
16431             try{
16432                 delete window[trans.cb];
16433             }catch(e){}
16434         }else{
16435             // if hasn't been loaded, wait for load to remove it to prevent script error
16436             window[trans.cb] = function(){
16437                 window[trans.cb] = undefined;
16438                 try{
16439                     delete window[trans.cb];
16440                 }catch(e){}
16441             };
16442         }
16443     },
16444
16445     // private
16446     handleResponse : function(o, trans){
16447         this.trans = false;
16448         this.destroyTrans(trans, true);
16449         var result;
16450         try {
16451             result = trans.reader.readRecords(o);
16452         }catch(e){
16453             this.fireEvent("loadexception", this, o, trans.arg, e);
16454             trans.callback.call(trans.scope||window, null, trans.arg, false);
16455             return;
16456         }
16457         this.fireEvent("load", this, o, trans.arg);
16458         trans.callback.call(trans.scope||window, result, trans.arg, true);
16459     },
16460
16461     // private
16462     handleFailure : function(trans){
16463         this.trans = false;
16464         this.destroyTrans(trans, false);
16465         this.fireEvent("loadexception", this, null, trans.arg);
16466         trans.callback.call(trans.scope||window, null, trans.arg, false);
16467     }
16468 });/*
16469  * Based on:
16470  * Ext JS Library 1.1.1
16471  * Copyright(c) 2006-2007, Ext JS, LLC.
16472  *
16473  * Originally Released Under LGPL - original licence link has changed is not relivant.
16474  *
16475  * Fork - LGPL
16476  * <script type="text/javascript">
16477  */
16478
16479 /**
16480  * @class Roo.data.JsonReader
16481  * @extends Roo.data.DataReader
16482  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16483  * based on mappings in a provided Roo.data.Record constructor.
16484  * 
16485  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16486  * in the reply previously. 
16487  * 
16488  * <p>
16489  * Example code:
16490  * <pre><code>
16491 var RecordDef = Roo.data.Record.create([
16492     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16493     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16494 ]);
16495 var myReader = new Roo.data.JsonReader({
16496     totalProperty: "results",    // The property which contains the total dataset size (optional)
16497     root: "rows",                // The property which contains an Array of row objects
16498     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16499 }, RecordDef);
16500 </code></pre>
16501  * <p>
16502  * This would consume a JSON file like this:
16503  * <pre><code>
16504 { 'results': 2, 'rows': [
16505     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16506     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16507 }
16508 </code></pre>
16509  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16510  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16511  * paged from the remote server.
16512  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16513  * @cfg {String} root name of the property which contains the Array of row objects.
16514  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16515  * @cfg {Array} fields Array of field definition objects
16516  * @constructor
16517  * Create a new JsonReader
16518  * @param {Object} meta Metadata configuration options
16519  * @param {Object} recordType Either an Array of field definition objects,
16520  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16521  */
16522 Roo.data.JsonReader = function(meta, recordType){
16523     
16524     meta = meta || {};
16525     // set some defaults:
16526     Roo.applyIf(meta, {
16527         totalProperty: 'total',
16528         successProperty : 'success',
16529         root : 'data',
16530         id : 'id'
16531     });
16532     
16533     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16534 };
16535 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16536     
16537     readerType : 'Json',
16538     
16539     /**
16540      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16541      * Used by Store query builder to append _requestMeta to params.
16542      * 
16543      */
16544     metaFromRemote : false,
16545     /**
16546      * This method is only used by a DataProxy which has retrieved data from a remote server.
16547      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16548      * @return {Object} data A data block which is used by an Roo.data.Store object as
16549      * a cache of Roo.data.Records.
16550      */
16551     read : function(response){
16552         var json = response.responseText;
16553        
16554         var o = /* eval:var:o */ eval("("+json+")");
16555         if(!o) {
16556             throw {message: "JsonReader.read: Json object not found"};
16557         }
16558         
16559         if(o.metaData){
16560             
16561             delete this.ef;
16562             this.metaFromRemote = true;
16563             this.meta = o.metaData;
16564             this.recordType = Roo.data.Record.create(o.metaData.fields);
16565             this.onMetaChange(this.meta, this.recordType, o);
16566         }
16567         return this.readRecords(o);
16568     },
16569
16570     // private function a store will implement
16571     onMetaChange : function(meta, recordType, o){
16572
16573     },
16574
16575     /**
16576          * @ignore
16577          */
16578     simpleAccess: function(obj, subsc) {
16579         return obj[subsc];
16580     },
16581
16582         /**
16583          * @ignore
16584          */
16585     getJsonAccessor: function(){
16586         var re = /[\[\.]/;
16587         return function(expr) {
16588             try {
16589                 return(re.test(expr))
16590                     ? new Function("obj", "return obj." + expr)
16591                     : function(obj){
16592                         return obj[expr];
16593                     };
16594             } catch(e){}
16595             return Roo.emptyFn;
16596         };
16597     }(),
16598
16599     /**
16600      * Create a data block containing Roo.data.Records from an XML document.
16601      * @param {Object} o An object which contains an Array of row objects in the property specified
16602      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16603      * which contains the total size of the dataset.
16604      * @return {Object} data A data block which is used by an Roo.data.Store object as
16605      * a cache of Roo.data.Records.
16606      */
16607     readRecords : function(o){
16608         /**
16609          * After any data loads, the raw JSON data is available for further custom processing.
16610          * @type Object
16611          */
16612         this.o = o;
16613         var s = this.meta, Record = this.recordType,
16614             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16615
16616 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16617         if (!this.ef) {
16618             if(s.totalProperty) {
16619                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16620                 }
16621                 if(s.successProperty) {
16622                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16623                 }
16624                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16625                 if (s.id) {
16626                         var g = this.getJsonAccessor(s.id);
16627                         this.getId = function(rec) {
16628                                 var r = g(rec);  
16629                                 return (r === undefined || r === "") ? null : r;
16630                         };
16631                 } else {
16632                         this.getId = function(){return null;};
16633                 }
16634             this.ef = [];
16635             for(var jj = 0; jj < fl; jj++){
16636                 f = fi[jj];
16637                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16638                 this.ef[jj] = this.getJsonAccessor(map);
16639             }
16640         }
16641
16642         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16643         if(s.totalProperty){
16644             var vt = parseInt(this.getTotal(o), 10);
16645             if(!isNaN(vt)){
16646                 totalRecords = vt;
16647             }
16648         }
16649         if(s.successProperty){
16650             var vs = this.getSuccess(o);
16651             if(vs === false || vs === 'false'){
16652                 success = false;
16653             }
16654         }
16655         var records = [];
16656         for(var i = 0; i < c; i++){
16657                 var n = root[i];
16658             var values = {};
16659             var id = this.getId(n);
16660             for(var j = 0; j < fl; j++){
16661                 f = fi[j];
16662             var v = this.ef[j](n);
16663             if (!f.convert) {
16664                 Roo.log('missing convert for ' + f.name);
16665                 Roo.log(f);
16666                 continue;
16667             }
16668             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16669             }
16670             var record = new Record(values, id);
16671             record.json = n;
16672             records[i] = record;
16673         }
16674         return {
16675             raw : o,
16676             success : success,
16677             records : records,
16678             totalRecords : totalRecords
16679         };
16680     },
16681     // used when loading children.. @see loadDataFromChildren
16682     toLoadData: function(rec)
16683     {
16684         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16685         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16686         return { data : data, total : data.length };
16687         
16688     }
16689 });/*
16690  * Based on:
16691  * Ext JS Library 1.1.1
16692  * Copyright(c) 2006-2007, Ext JS, LLC.
16693  *
16694  * Originally Released Under LGPL - original licence link has changed is not relivant.
16695  *
16696  * Fork - LGPL
16697  * <script type="text/javascript">
16698  */
16699
16700 /**
16701  * @class Roo.data.ArrayReader
16702  * @extends Roo.data.DataReader
16703  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16704  * Each element of that Array represents a row of data fields. The
16705  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16706  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16707  * <p>
16708  * Example code:.
16709  * <pre><code>
16710 var RecordDef = Roo.data.Record.create([
16711     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16712     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16713 ]);
16714 var myReader = new Roo.data.ArrayReader({
16715     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16716 }, RecordDef);
16717 </code></pre>
16718  * <p>
16719  * This would consume an Array like this:
16720  * <pre><code>
16721 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16722   </code></pre>
16723  
16724  * @constructor
16725  * Create a new JsonReader
16726  * @param {Object} meta Metadata configuration options.
16727  * @param {Object|Array} recordType Either an Array of field definition objects
16728  * 
16729  * @cfg {Array} fields Array of field definition objects
16730  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16731  * as specified to {@link Roo.data.Record#create},
16732  * or an {@link Roo.data.Record} object
16733  *
16734  * 
16735  * created using {@link Roo.data.Record#create}.
16736  */
16737 Roo.data.ArrayReader = function(meta, recordType)
16738 {    
16739     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16740 };
16741
16742 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16743     
16744       /**
16745      * Create a data block containing Roo.data.Records from an XML document.
16746      * @param {Object} o An Array of row objects which represents the dataset.
16747      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16748      * a cache of Roo.data.Records.
16749      */
16750     readRecords : function(o)
16751     {
16752         var sid = this.meta ? this.meta.id : null;
16753         var recordType = this.recordType, fields = recordType.prototype.fields;
16754         var records = [];
16755         var root = o;
16756         for(var i = 0; i < root.length; i++){
16757             var n = root[i];
16758             var values = {};
16759             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16760             for(var j = 0, jlen = fields.length; j < jlen; j++){
16761                 var f = fields.items[j];
16762                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16763                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16764                 v = f.convert(v);
16765                 values[f.name] = v;
16766             }
16767             var record = new recordType(values, id);
16768             record.json = n;
16769             records[records.length] = record;
16770         }
16771         return {
16772             records : records,
16773             totalRecords : records.length
16774         };
16775     },
16776     // used when loading children.. @see loadDataFromChildren
16777     toLoadData: function(rec)
16778     {
16779         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16780         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16781         
16782     }
16783     
16784     
16785 });/*
16786  * - LGPL
16787  * * 
16788  */
16789
16790 /**
16791  * @class Roo.bootstrap.form.ComboBox
16792  * @extends Roo.bootstrap.form.TriggerField
16793  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16794  * @cfg {Boolean} append (true|false) default false
16795  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16796  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16797  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16798  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16799  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16800  * @cfg {Boolean} animate default true
16801  * @cfg {Boolean} emptyResultText only for touch device
16802  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16803  * @cfg {String} emptyTitle default ''
16804  * @cfg {Number} width fixed with? experimental
16805  * @constructor
16806  * Create a new ComboBox.
16807  * @param {Object} config Configuration options
16808  */
16809 Roo.bootstrap.form.ComboBox = function(config){
16810     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16811     this.addEvents({
16812         /**
16813          * @event expand
16814          * Fires when the dropdown list is expanded
16815         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16816         */
16817         'expand' : true,
16818         /**
16819          * @event collapse
16820          * Fires when the dropdown list is collapsed
16821         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16822         */
16823         'collapse' : true,
16824         /**
16825          * @event beforeselect
16826          * Fires before a list item is selected. Return false to cancel the selection.
16827         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16828         * @param {Roo.data.Record} record The data record returned from the underlying store
16829         * @param {Number} index The index of the selected item in the dropdown list
16830         */
16831         'beforeselect' : true,
16832         /**
16833          * @event select
16834          * Fires when a list item is selected
16835         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16836         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16837         * @param {Number} index The index of the selected item in the dropdown list
16838         */
16839         'select' : true,
16840         /**
16841          * @event beforequery
16842          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16843          * The event object passed has these properties:
16844         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16845         * @param {String} query The query
16846         * @param {Boolean} forceAll true to force "all" query
16847         * @param {Boolean} cancel true to cancel the query
16848         * @param {Object} e The query event object
16849         */
16850         'beforequery': true,
16851          /**
16852          * @event add
16853          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16854         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16855         */
16856         'add' : true,
16857         /**
16858          * @event edit
16859          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16860         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16861         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16862         */
16863         'edit' : true,
16864         /**
16865          * @event remove
16866          * Fires when the remove value from the combobox array
16867         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16868         */
16869         'remove' : true,
16870         /**
16871          * @event afterremove
16872          * Fires when the remove value from the combobox array
16873         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16874         */
16875         'afterremove' : true,
16876         /**
16877          * @event specialfilter
16878          * Fires when specialfilter
16879             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16880             */
16881         'specialfilter' : true,
16882         /**
16883          * @event tick
16884          * Fires when tick the element
16885             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16886             */
16887         'tick' : true,
16888         /**
16889          * @event touchviewdisplay
16890          * Fires when touch view require special display (default is using displayField)
16891             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16892             * @param {Object} cfg set html .
16893             */
16894         'touchviewdisplay' : true
16895         
16896     });
16897     
16898     this.item = [];
16899     this.tickItems = [];
16900     
16901     this.selectedIndex = -1;
16902     if(this.mode == 'local'){
16903         if(config.queryDelay === undefined){
16904             this.queryDelay = 10;
16905         }
16906         if(config.minChars === undefined){
16907             this.minChars = 0;
16908         }
16909     }
16910 };
16911
16912 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16913      
16914     /**
16915      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16916      * rendering into an Roo.Editor, defaults to false)
16917      */
16918     /**
16919      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16920      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16921      */
16922     /**
16923      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16924      */
16925     /**
16926      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16927      * the dropdown list (defaults to undefined, with no header element)
16928      */
16929
16930      /**
16931      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16932      */
16933      
16934      /**
16935      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16936      */
16937     listWidth: undefined,
16938     /**
16939      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16940      * mode = 'remote' or 'text' if mode = 'local')
16941      */
16942     displayField: undefined,
16943     
16944     /**
16945      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16946      * mode = 'remote' or 'value' if mode = 'local'). 
16947      * Note: use of a valueField requires the user make a selection
16948      * in order for a value to be mapped.
16949      */
16950     valueField: undefined,
16951     /**
16952      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16953      */
16954     modalTitle : '',
16955     
16956     /**
16957      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16958      * field's data value (defaults to the underlying DOM element's name)
16959      */
16960     hiddenName: undefined,
16961     /**
16962      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16963      */
16964     listClass: '',
16965     /**
16966      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16967      */
16968     selectedClass: 'active',
16969     
16970     /**
16971      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16972      */
16973     shadow:'sides',
16974     /**
16975      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16976      * anchor positions (defaults to 'tl-bl')
16977      */
16978     listAlign: 'tl-bl?',
16979     /**
16980      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16981      */
16982     maxHeight: 300,
16983     /**
16984      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16985      * query specified by the allQuery config option (defaults to 'query')
16986      */
16987     triggerAction: 'query',
16988     /**
16989      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16990      * (defaults to 4, does not apply if editable = false)
16991      */
16992     minChars : 4,
16993     /**
16994      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16995      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16996      */
16997     typeAhead: false,
16998     /**
16999      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17000      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17001      */
17002     queryDelay: 500,
17003     /**
17004      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17005      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17006      */
17007     pageSize: 0,
17008     /**
17009      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17010      * when editable = true (defaults to false)
17011      */
17012     selectOnFocus:false,
17013     /**
17014      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17015      */
17016     queryParam: 'query',
17017     /**
17018      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17019      * when mode = 'remote' (defaults to 'Loading...')
17020      */
17021     loadingText: 'Loading...',
17022     /**
17023      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17024      */
17025     resizable: false,
17026     /**
17027      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17028      */
17029     handleHeight : 8,
17030     /**
17031      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17032      * traditional select (defaults to true)
17033      */
17034     editable: true,
17035     /**
17036      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17037      */
17038     allQuery: '',
17039     /**
17040      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17041      */
17042     mode: 'remote',
17043     /**
17044      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17045      * listWidth has a higher value)
17046      */
17047     minListWidth : 70,
17048     /**
17049      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17050      * allow the user to set arbitrary text into the field (defaults to false)
17051      */
17052     forceSelection:false,
17053     /**
17054      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17055      * if typeAhead = true (defaults to 250)
17056      */
17057     typeAheadDelay : 250,
17058     /**
17059      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17060      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17061      */
17062     valueNotFoundText : undefined,
17063     /**
17064      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17065      */
17066     blockFocus : false,
17067     
17068     /**
17069      * @cfg {Boolean} disableClear Disable showing of clear button.
17070      */
17071     disableClear : false,
17072     /**
17073      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17074      */
17075     alwaysQuery : false,
17076     
17077     /**
17078      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17079      */
17080     multiple : false,
17081     
17082     /**
17083      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17084      */
17085     invalidClass : "has-warning",
17086     
17087     /**
17088      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17089      */
17090     validClass : "has-success",
17091     
17092     /**
17093      * @cfg {Boolean} specialFilter (true|false) special filter default false
17094      */
17095     specialFilter : false,
17096     
17097     /**
17098      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17099      */
17100     mobileTouchView : true,
17101     
17102     /**
17103      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17104      */
17105     useNativeIOS : false,
17106     
17107     /**
17108      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17109      */
17110     mobile_restrict_height : false,
17111     
17112     ios_options : false,
17113     
17114     //private
17115     addicon : false,
17116     editicon: false,
17117     
17118     page: 0,
17119     hasQuery: false,
17120     append: false,
17121     loadNext: false,
17122     autoFocus : true,
17123     tickable : false,
17124     btnPosition : 'right',
17125     triggerList : true,
17126     showToggleBtn : true,
17127     animate : true,
17128     emptyResultText: 'Empty',
17129     triggerText : 'Select',
17130     emptyTitle : '',
17131     width : false,
17132     
17133     // element that contains real text value.. (when hidden is used..)
17134     
17135     getAutoCreate : function()
17136     {   
17137         var cfg = false;
17138         //render
17139         /*
17140          * Render classic select for iso
17141          */
17142         
17143         if(Roo.isIOS && this.useNativeIOS){
17144             cfg = this.getAutoCreateNativeIOS();
17145             return cfg;
17146         }
17147         
17148         /*
17149          * Touch Devices
17150          */
17151         
17152         if(Roo.isTouch && this.mobileTouchView){
17153             cfg = this.getAutoCreateTouchView();
17154             return cfg;;
17155         }
17156         
17157         /*
17158          *  Normal ComboBox
17159          */
17160         if(!this.tickable){
17161             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17162             return cfg;
17163         }
17164         
17165         /*
17166          *  ComboBox with tickable selections
17167          */
17168              
17169         var align = this.labelAlign || this.parentLabelAlign();
17170         
17171         cfg = {
17172             cls : 'form-group roo-combobox-tickable' //input-group
17173         };
17174         
17175         var btn_text_select = '';
17176         var btn_text_done = '';
17177         var btn_text_cancel = '';
17178         
17179         if (this.btn_text_show) {
17180             btn_text_select = 'Select';
17181             btn_text_done = 'Done';
17182             btn_text_cancel = 'Cancel'; 
17183         }
17184         
17185         var buttons = {
17186             tag : 'div',
17187             cls : 'tickable-buttons',
17188             cn : [
17189                 {
17190                     tag : 'button',
17191                     type : 'button',
17192                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17193                     //html : this.triggerText
17194                     html: btn_text_select
17195                 },
17196                 {
17197                     tag : 'button',
17198                     type : 'button',
17199                     name : 'ok',
17200                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17201                     //html : 'Done'
17202                     html: btn_text_done
17203                 },
17204                 {
17205                     tag : 'button',
17206                     type : 'button',
17207                     name : 'cancel',
17208                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17209                     //html : 'Cancel'
17210                     html: btn_text_cancel
17211                 }
17212             ]
17213         };
17214         
17215         if(this.editable){
17216             buttons.cn.unshift({
17217                 tag: 'input',
17218                 cls: 'roo-select2-search-field-input'
17219             });
17220         }
17221         
17222         var _this = this;
17223         
17224         Roo.each(buttons.cn, function(c){
17225             if (_this.size) {
17226                 c.cls += ' btn-' + _this.size;
17227             }
17228
17229             if (_this.disabled) {
17230                 c.disabled = true;
17231             }
17232         });
17233         
17234         var box = {
17235             tag: 'div',
17236             style : 'display: contents',
17237             cn: [
17238                 {
17239                     tag: 'input',
17240                     type : 'hidden',
17241                     cls: 'form-hidden-field'
17242                 },
17243                 {
17244                     tag: 'ul',
17245                     cls: 'roo-select2-choices',
17246                     cn:[
17247                         {
17248                             tag: 'li',
17249                             cls: 'roo-select2-search-field',
17250                             cn: [
17251                                 buttons
17252                             ]
17253                         }
17254                     ]
17255                 }
17256             ]
17257         };
17258         
17259         var combobox = {
17260             cls: 'roo-select2-container input-group roo-select2-container-multi',
17261             cn: [
17262                 
17263                 box
17264 //                {
17265 //                    tag: 'ul',
17266 //                    cls: 'typeahead typeahead-long dropdown-menu',
17267 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17268 //                }
17269             ]
17270         };
17271         
17272         if(this.hasFeedback && !this.allowBlank){
17273             
17274             var feedback = {
17275                 tag: 'span',
17276                 cls: 'glyphicon form-control-feedback'
17277             };
17278
17279             combobox.cn.push(feedback);
17280         }
17281         
17282         
17283         
17284         var indicator = {
17285             tag : 'i',
17286             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17287             tooltip : 'This field is required'
17288         };
17289         if (Roo.bootstrap.version == 4) {
17290             indicator = {
17291                 tag : 'i',
17292                 style : 'display:none'
17293             };
17294         }
17295         if (align ==='left' && this.fieldLabel.length) {
17296             
17297             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17298             
17299             cfg.cn = [
17300                 indicator,
17301                 {
17302                     tag: 'label',
17303                     'for' :  id,
17304                     cls : 'control-label col-form-label',
17305                     html : this.fieldLabel
17306
17307                 },
17308                 {
17309                     cls : "", 
17310                     cn: [
17311                         combobox
17312                     ]
17313                 }
17314
17315             ];
17316             
17317             var labelCfg = cfg.cn[1];
17318             var contentCfg = cfg.cn[2];
17319             
17320
17321             if(this.indicatorpos == 'right'){
17322                 
17323                 cfg.cn = [
17324                     {
17325                         tag: 'label',
17326                         'for' :  id,
17327                         cls : 'control-label col-form-label',
17328                         cn : [
17329                             {
17330                                 tag : 'span',
17331                                 html : this.fieldLabel
17332                             },
17333                             indicator
17334                         ]
17335                     },
17336                     {
17337                         cls : "",
17338                         cn: [
17339                             combobox
17340                         ]
17341                     }
17342
17343                 ];
17344                 
17345                 
17346                 
17347                 labelCfg = cfg.cn[0];
17348                 contentCfg = cfg.cn[1];
17349             
17350             }
17351             
17352             if(this.labelWidth > 12){
17353                 labelCfg.style = "width: " + this.labelWidth + 'px';
17354             }
17355             if(this.width * 1 > 0){
17356                 contentCfg.style = "width: " + this.width + 'px';
17357             }
17358             if(this.labelWidth < 13 && this.labelmd == 0){
17359                 this.labelmd = this.labelWidth;
17360             }
17361             
17362             if(this.labellg > 0){
17363                 labelCfg.cls += ' col-lg-' + this.labellg;
17364                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17365             }
17366             
17367             if(this.labelmd > 0){
17368                 labelCfg.cls += ' col-md-' + this.labelmd;
17369                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17370             }
17371             
17372             if(this.labelsm > 0){
17373                 labelCfg.cls += ' col-sm-' + this.labelsm;
17374                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17375             }
17376             
17377             if(this.labelxs > 0){
17378                 labelCfg.cls += ' col-xs-' + this.labelxs;
17379                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17380             }
17381                 
17382                 
17383         } else if ( this.fieldLabel.length) {
17384 //                Roo.log(" label");
17385                  cfg.cn = [
17386                    indicator,
17387                     {
17388                         tag: 'label',
17389                         //cls : 'input-group-addon',
17390                         html : this.fieldLabel
17391                     },
17392                     combobox
17393                 ];
17394                 
17395                 if(this.indicatorpos == 'right'){
17396                     cfg.cn = [
17397                         {
17398                             tag: 'label',
17399                             //cls : 'input-group-addon',
17400                             html : this.fieldLabel
17401                         },
17402                         indicator,
17403                         combobox
17404                     ];
17405                     
17406                 }
17407
17408         } else {
17409             
17410 //                Roo.log(" no label && no align");
17411                 cfg = combobox
17412                      
17413                 
17414         }
17415          
17416         var settings=this;
17417         ['xs','sm','md','lg'].map(function(size){
17418             if (settings[size]) {
17419                 cfg.cls += ' col-' + size + '-' + settings[size];
17420             }
17421         });
17422         
17423         return cfg;
17424         
17425     },
17426     
17427     _initEventsCalled : false,
17428     
17429     // private
17430     initEvents: function()
17431     {   
17432         if (this._initEventsCalled) { // as we call render... prevent looping...
17433             return;
17434         }
17435         this._initEventsCalled = true;
17436         
17437         if (!this.store) {
17438             throw "can not find store for combo";
17439         }
17440         
17441         this.indicator = this.indicatorEl();
17442         
17443         this.store = Roo.factory(this.store, Roo.data);
17444         this.store.parent = this;
17445         
17446         // if we are building from html. then this element is so complex, that we can not really
17447         // use the rendered HTML.
17448         // so we have to trash and replace the previous code.
17449         if (Roo.XComponent.build_from_html) {
17450             // remove this element....
17451             var e = this.el.dom, k=0;
17452             while (e ) { e = e.previousSibling;  ++k;}
17453
17454             this.el.remove();
17455             
17456             this.el=false;
17457             this.rendered = false;
17458             
17459             this.render(this.parent().getChildContainer(true), k);
17460         }
17461         
17462         if(Roo.isIOS && this.useNativeIOS){
17463             this.initIOSView();
17464             return;
17465         }
17466         
17467         /*
17468          * Touch Devices
17469          */
17470         
17471         if(Roo.isTouch && this.mobileTouchView){
17472             this.initTouchView();
17473             return;
17474         }
17475         
17476         if(this.tickable){
17477             this.initTickableEvents();
17478             return;
17479         }
17480         
17481         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17482         
17483         if(this.hiddenName){
17484             
17485             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17486             
17487             this.hiddenField.dom.value =
17488                 this.hiddenValue !== undefined ? this.hiddenValue :
17489                 this.value !== undefined ? this.value : '';
17490
17491             // prevent input submission
17492             this.el.dom.removeAttribute('name');
17493             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17494              
17495              
17496         }
17497         //if(Roo.isGecko){
17498         //    this.el.dom.setAttribute('autocomplete', 'off');
17499         //}
17500         
17501         var cls = 'x-combo-list';
17502         
17503         //this.list = new Roo.Layer({
17504         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17505         //});
17506         
17507         var _this = this;
17508         
17509         (function(){
17510             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17511             _this.list.setWidth(lw);
17512         }).defer(100);
17513         
17514         this.list.on('mouseover', this.onViewOver, this);
17515         this.list.on('mousemove', this.onViewMove, this);
17516         this.list.on('scroll', this.onViewScroll, this);
17517         
17518         /*
17519         this.list.swallowEvent('mousewheel');
17520         this.assetHeight = 0;
17521
17522         if(this.title){
17523             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17524             this.assetHeight += this.header.getHeight();
17525         }
17526
17527         this.innerList = this.list.createChild({cls:cls+'-inner'});
17528         this.innerList.on('mouseover', this.onViewOver, this);
17529         this.innerList.on('mousemove', this.onViewMove, this);
17530         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17531         
17532         if(this.allowBlank && !this.pageSize && !this.disableClear){
17533             this.footer = this.list.createChild({cls:cls+'-ft'});
17534             this.pageTb = new Roo.Toolbar(this.footer);
17535            
17536         }
17537         if(this.pageSize){
17538             this.footer = this.list.createChild({cls:cls+'-ft'});
17539             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17540                     {pageSize: this.pageSize});
17541             
17542         }
17543         
17544         if (this.pageTb && this.allowBlank && !this.disableClear) {
17545             var _this = this;
17546             this.pageTb.add(new Roo.Toolbar.Fill(), {
17547                 cls: 'x-btn-icon x-btn-clear',
17548                 text: '&#160;',
17549                 handler: function()
17550                 {
17551                     _this.collapse();
17552                     _this.clearValue();
17553                     _this.onSelect(false, -1);
17554                 }
17555             });
17556         }
17557         if (this.footer) {
17558             this.assetHeight += this.footer.getHeight();
17559         }
17560         */
17561             
17562         if(!this.tpl){
17563             this.tpl = Roo.bootstrap.version == 4 ?
17564                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17565                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17566         }
17567
17568         this.view = new Roo.View(this.list, this.tpl, {
17569             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17570         });
17571         //this.view.wrapEl.setDisplayed(false);
17572         this.view.on('click', this.onViewClick, this);
17573         
17574         
17575         this.store.on('beforeload', this.onBeforeLoad, this);
17576         this.store.on('load', this.onLoad, this);
17577         this.store.on('loadexception', this.onLoadException, this);
17578         /*
17579         if(this.resizable){
17580             this.resizer = new Roo.Resizable(this.list,  {
17581                pinned:true, handles:'se'
17582             });
17583             this.resizer.on('resize', function(r, w, h){
17584                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17585                 this.listWidth = w;
17586                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17587                 this.restrictHeight();
17588             }, this);
17589             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17590         }
17591         */
17592         if(!this.editable){
17593             this.editable = true;
17594             this.setEditable(false);
17595         }
17596         
17597         /*
17598         
17599         if (typeof(this.events.add.listeners) != 'undefined') {
17600             
17601             this.addicon = this.wrap.createChild(
17602                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17603        
17604             this.addicon.on('click', function(e) {
17605                 this.fireEvent('add', this);
17606             }, this);
17607         }
17608         if (typeof(this.events.edit.listeners) != 'undefined') {
17609             
17610             this.editicon = this.wrap.createChild(
17611                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17612             if (this.addicon) {
17613                 this.editicon.setStyle('margin-left', '40px');
17614             }
17615             this.editicon.on('click', function(e) {
17616                 
17617                 // we fire even  if inothing is selected..
17618                 this.fireEvent('edit', this, this.lastData );
17619                 
17620             }, this);
17621         }
17622         */
17623         
17624         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17625             "up" : function(e){
17626                 this.inKeyMode = true;
17627                 this.selectPrev();
17628             },
17629
17630             "down" : function(e){
17631                 if(!this.isExpanded()){
17632                     this.onTriggerClick();
17633                 }else{
17634                     this.inKeyMode = true;
17635                     this.selectNext();
17636                 }
17637             },
17638
17639             "enter" : function(e){
17640 //                this.onViewClick();
17641                 //return true;
17642                 this.collapse();
17643                 
17644                 if(this.fireEvent("specialkey", this, e)){
17645                     this.onViewClick(false);
17646                 }
17647                 
17648                 return true;
17649             },
17650
17651             "esc" : function(e){
17652                 this.collapse();
17653             },
17654
17655             "tab" : function(e){
17656                 this.collapse();
17657                 
17658                 if(this.fireEvent("specialkey", this, e)){
17659                     this.onViewClick(false);
17660                 }
17661                 
17662                 return true;
17663             },
17664
17665             scope : this,
17666
17667             doRelay : function(foo, bar, hname){
17668                 if(hname == 'down' || this.scope.isExpanded()){
17669                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17670                 }
17671                 return true;
17672             },
17673
17674             forceKeyDown: true
17675         });
17676         
17677         
17678         this.queryDelay = Math.max(this.queryDelay || 10,
17679                 this.mode == 'local' ? 10 : 250);
17680         
17681         
17682         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17683         
17684         if(this.typeAhead){
17685             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17686         }
17687         if(this.editable !== false){
17688             this.inputEl().on("keyup", this.onKeyUp, this);
17689         }
17690         if(this.forceSelection){
17691             this.inputEl().on('blur', this.doForce, this);
17692         }
17693         
17694         if(this.multiple){
17695             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17696             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17697         }
17698     },
17699     
17700     initTickableEvents: function()
17701     {   
17702         this.createList();
17703         
17704         if(this.hiddenName){
17705             
17706             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17707             
17708             this.hiddenField.dom.value =
17709                 this.hiddenValue !== undefined ? this.hiddenValue :
17710                 this.value !== undefined ? this.value : '';
17711
17712             // prevent input submission
17713             this.el.dom.removeAttribute('name');
17714             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17715              
17716              
17717         }
17718         
17719 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17720         
17721         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17722         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17723         if(this.triggerList){
17724             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17725         }
17726          
17727         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17728         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17729         
17730         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17731         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17732         
17733         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17734         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17735         
17736         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17737         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17738         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17739         
17740         this.okBtn.hide();
17741         this.cancelBtn.hide();
17742         
17743         var _this = this;
17744         
17745         (function(){
17746             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17747             _this.list.setWidth(lw);
17748         }).defer(100);
17749         
17750         this.list.on('mouseover', this.onViewOver, this);
17751         this.list.on('mousemove', this.onViewMove, this);
17752         
17753         this.list.on('scroll', this.onViewScroll, this);
17754         
17755         if(!this.tpl){
17756             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17757                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17758         }
17759
17760         this.view = new Roo.View(this.list, this.tpl, {
17761             singleSelect:true,
17762             tickable:true,
17763             parent:this,
17764             store: this.store,
17765             selectedClass: this.selectedClass
17766         });
17767         
17768         //this.view.wrapEl.setDisplayed(false);
17769         this.view.on('click', this.onViewClick, this);
17770         
17771         
17772         
17773         this.store.on('beforeload', this.onBeforeLoad, this);
17774         this.store.on('load', this.onLoad, this);
17775         this.store.on('loadexception', this.onLoadException, this);
17776         
17777         if(this.editable){
17778             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17779                 "up" : function(e){
17780                     this.inKeyMode = true;
17781                     this.selectPrev();
17782                 },
17783
17784                 "down" : function(e){
17785                     this.inKeyMode = true;
17786                     this.selectNext();
17787                 },
17788
17789                 "enter" : function(e){
17790                     if(this.fireEvent("specialkey", this, e)){
17791                         this.onViewClick(false);
17792                     }
17793                     
17794                     return true;
17795                 },
17796
17797                 "esc" : function(e){
17798                     this.onTickableFooterButtonClick(e, false, false);
17799                 },
17800
17801                 "tab" : function(e){
17802                     this.fireEvent("specialkey", this, e);
17803                     
17804                     this.onTickableFooterButtonClick(e, false, false);
17805                     
17806                     return true;
17807                 },
17808
17809                 scope : this,
17810
17811                 doRelay : function(e, fn, key){
17812                     if(this.scope.isExpanded()){
17813                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17814                     }
17815                     return true;
17816                 },
17817
17818                 forceKeyDown: true
17819             });
17820         }
17821         
17822         this.queryDelay = Math.max(this.queryDelay || 10,
17823                 this.mode == 'local' ? 10 : 250);
17824         
17825         
17826         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17827         
17828         if(this.typeAhead){
17829             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17830         }
17831         
17832         if(this.editable !== false){
17833             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17834         }
17835         
17836         this.indicator = this.indicatorEl();
17837         
17838         if(this.indicator){
17839             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17840             this.indicator.hide();
17841         }
17842         
17843     },
17844
17845     onDestroy : function(){
17846         if(this.view){
17847             this.view.setStore(null);
17848             this.view.el.removeAllListeners();
17849             this.view.el.remove();
17850             this.view.purgeListeners();
17851         }
17852         if(this.list){
17853             this.list.dom.innerHTML  = '';
17854         }
17855         
17856         if(this.store){
17857             this.store.un('beforeload', this.onBeforeLoad, this);
17858             this.store.un('load', this.onLoad, this);
17859             this.store.un('loadexception', this.onLoadException, this);
17860         }
17861         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17862     },
17863
17864     // private
17865     fireKey : function(e){
17866         if(e.isNavKeyPress() && !this.list.isVisible()){
17867             this.fireEvent("specialkey", this, e);
17868         }
17869     },
17870
17871     // private
17872     onResize: function(w, h)
17873     {
17874         
17875         
17876 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17877 //        
17878 //        if(typeof w != 'number'){
17879 //            // we do not handle it!?!?
17880 //            return;
17881 //        }
17882 //        var tw = this.trigger.getWidth();
17883 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17884 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17885 //        var x = w - tw;
17886 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17887 //            
17888 //        //this.trigger.setStyle('left', x+'px');
17889 //        
17890 //        if(this.list && this.listWidth === undefined){
17891 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17892 //            this.list.setWidth(lw);
17893 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17894 //        }
17895         
17896     
17897         
17898     },
17899
17900     /**
17901      * Allow or prevent the user from directly editing the field text.  If false is passed,
17902      * the user will only be able to select from the items defined in the dropdown list.  This method
17903      * is the runtime equivalent of setting the 'editable' config option at config time.
17904      * @param {Boolean} value True to allow the user to directly edit the field text
17905      */
17906     setEditable : function(value){
17907         if(value == this.editable){
17908             return;
17909         }
17910         this.editable = value;
17911         if(!value){
17912             this.inputEl().dom.setAttribute('readOnly', true);
17913             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17914             this.inputEl().addClass('x-combo-noedit');
17915         }else{
17916             this.inputEl().dom.removeAttribute('readOnly');
17917             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17918             this.inputEl().removeClass('x-combo-noedit');
17919         }
17920     },
17921
17922     // private
17923     
17924     onBeforeLoad : function(combo,opts){
17925         if(!this.hasFocus){
17926             return;
17927         }
17928          if (!opts.add) {
17929             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17930          }
17931         this.restrictHeight();
17932         this.selectedIndex = -1;
17933     },
17934
17935     // private
17936     onLoad : function(){
17937         
17938         this.hasQuery = false;
17939         
17940         if(!this.hasFocus){
17941             return;
17942         }
17943         
17944         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17945             this.loading.hide();
17946         }
17947         
17948         if(this.store.getCount() > 0){
17949             
17950             this.expand();
17951             this.restrictHeight();
17952             if(this.lastQuery == this.allQuery){
17953                 if(this.editable && !this.tickable){
17954                     this.inputEl().dom.select();
17955                 }
17956                 
17957                 if(
17958                     !this.selectByValue(this.value, true) &&
17959                     this.autoFocus && 
17960                     (
17961                         !this.store.lastOptions ||
17962                         typeof(this.store.lastOptions.add) == 'undefined' || 
17963                         this.store.lastOptions.add != true
17964                     )
17965                 ){
17966                     this.select(0, true);
17967                 }
17968             }else{
17969                 if(this.autoFocus){
17970                     this.selectNext();
17971                 }
17972                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17973                     this.taTask.delay(this.typeAheadDelay);
17974                 }
17975             }
17976         }else{
17977             this.onEmptyResults();
17978         }
17979         
17980         //this.el.focus();
17981     },
17982     // private
17983     onLoadException : function()
17984     {
17985         this.hasQuery = false;
17986         
17987         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17988             this.loading.hide();
17989         }
17990         
17991         if(this.tickable && this.editable){
17992             return;
17993         }
17994         
17995         this.collapse();
17996         // only causes errors at present
17997         //Roo.log(this.store.reader.jsonData);
17998         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17999             // fixme
18000             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18001         //}
18002         
18003         
18004     },
18005     // private
18006     onTypeAhead : function(){
18007         if(this.store.getCount() > 0){
18008             var r = this.store.getAt(0);
18009             var newValue = r.data[this.displayField];
18010             var len = newValue.length;
18011             var selStart = this.getRawValue().length;
18012             
18013             if(selStart != len){
18014                 this.setRawValue(newValue);
18015                 this.selectText(selStart, newValue.length);
18016             }
18017         }
18018     },
18019
18020     // private
18021     onSelect : function(record, index){
18022         
18023         if(this.fireEvent('beforeselect', this, record, index) !== false){
18024         
18025             this.setFromData(index > -1 ? record.data : false);
18026             
18027             this.collapse();
18028             this.fireEvent('select', this, record, index);
18029         }
18030     },
18031
18032     /**
18033      * Returns the currently selected field value or empty string if no value is set.
18034      * @return {String} value The selected value
18035      */
18036     getValue : function()
18037     {
18038         if(Roo.isIOS && this.useNativeIOS){
18039             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18040         }
18041         
18042         if(this.multiple){
18043             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18044         }
18045         
18046         if(this.valueField){
18047             return typeof this.value != 'undefined' ? this.value : '';
18048         }else{
18049             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18050         }
18051     },
18052     
18053     getRawValue : function()
18054     {
18055         if(Roo.isIOS && this.useNativeIOS){
18056             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18057         }
18058         
18059         var v = this.inputEl().getValue();
18060         
18061         return v;
18062     },
18063
18064     /**
18065      * Clears any text/value currently set in the field
18066      */
18067     clearValue : function(){
18068         
18069         if(this.hiddenField){
18070             this.hiddenField.dom.value = '';
18071         }
18072         this.value = '';
18073         this.setRawValue('');
18074         this.lastSelectionText = '';
18075         this.lastData = false;
18076         
18077         var close = this.closeTriggerEl();
18078         
18079         if(close){
18080             close.hide();
18081         }
18082         
18083         this.validate();
18084         
18085     },
18086
18087     /**
18088      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18089      * will be displayed in the field.  If the value does not match the data value of an existing item,
18090      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18091      * Otherwise the field will be blank (although the value will still be set).
18092      * @param {String} value The value to match
18093      */
18094     setValue : function(v)
18095     {
18096         if(Roo.isIOS && this.useNativeIOS){
18097             this.setIOSValue(v);
18098             return;
18099         }
18100         
18101         if(this.multiple){
18102             this.syncValue();
18103             return;
18104         }
18105         
18106         var text = v;
18107         if(this.valueField){
18108             var r = this.findRecord(this.valueField, v);
18109             if(r){
18110                 text = r.data[this.displayField];
18111             }else if(this.valueNotFoundText !== undefined){
18112                 text = this.valueNotFoundText;
18113             }
18114         }
18115         this.lastSelectionText = text;
18116         if(this.hiddenField){
18117             this.hiddenField.dom.value = v;
18118         }
18119         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18120         this.value = v;
18121         
18122         var close = this.closeTriggerEl();
18123         
18124         if(close){
18125             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18126         }
18127         
18128         this.validate();
18129     },
18130     /**
18131      * @property {Object} the last set data for the element
18132      */
18133     
18134     lastData : false,
18135     /**
18136      * Sets the value of the field based on a object which is related to the record format for the store.
18137      * @param {Object} value the value to set as. or false on reset?
18138      */
18139     setFromData : function(o){
18140         
18141         if(this.multiple){
18142             this.addItem(o);
18143             return;
18144         }
18145             
18146         var dv = ''; // display value
18147         var vv = ''; // value value..
18148         this.lastData = o;
18149         if (this.displayField) {
18150             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18151         } else {
18152             // this is an error condition!!!
18153             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18154         }
18155         
18156         if(this.valueField){
18157             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18158         }
18159         
18160         var close = this.closeTriggerEl();
18161         
18162         if(close){
18163             if(dv.length || vv * 1 > 0){
18164                 close.show() ;
18165                 this.blockFocus=true;
18166             } else {
18167                 close.hide();
18168             }             
18169         }
18170         
18171         if(this.hiddenField){
18172             this.hiddenField.dom.value = vv;
18173             
18174             this.lastSelectionText = dv;
18175             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18176             this.value = vv;
18177             return;
18178         }
18179         // no hidden field.. - we store the value in 'value', but still display
18180         // display field!!!!
18181         this.lastSelectionText = dv;
18182         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18183         this.value = vv;
18184         
18185         
18186         
18187     },
18188     // private
18189     reset : function(){
18190         // overridden so that last data is reset..
18191         
18192         if(this.multiple){
18193             this.clearItem();
18194             return;
18195         }
18196         
18197         this.setValue(this.originalValue);
18198         //this.clearInvalid();
18199         this.lastData = false;
18200         if (this.view) {
18201             this.view.clearSelections();
18202         }
18203         
18204         this.validate();
18205     },
18206     // private
18207     findRecord : function(prop, value){
18208         var record;
18209         if(this.store.getCount() > 0){
18210             this.store.each(function(r){
18211                 if(r.data[prop] == value){
18212                     record = r;
18213                     return false;
18214                 }
18215                 return true;
18216             });
18217         }
18218         return record;
18219     },
18220     
18221     getName: function()
18222     {
18223         // returns hidden if it's set..
18224         if (!this.rendered) {return ''};
18225         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18226         
18227     },
18228     // private
18229     onViewMove : function(e, t){
18230         this.inKeyMode = false;
18231     },
18232
18233     // private
18234     onViewOver : function(e, t){
18235         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18236             return;
18237         }
18238         var item = this.view.findItemFromChild(t);
18239         
18240         if(item){
18241             var index = this.view.indexOf(item);
18242             this.select(index, false);
18243         }
18244     },
18245
18246     // private
18247     onViewClick : function(view, doFocus, el, e)
18248     {
18249         var index = this.view.getSelectedIndexes()[0];
18250         
18251         var r = this.store.getAt(index);
18252         
18253         if(this.tickable){
18254             
18255             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18256                 return;
18257             }
18258             
18259             var rm = false;
18260             var _this = this;
18261             
18262             Roo.each(this.tickItems, function(v,k){
18263                 
18264                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18265                     Roo.log(v);
18266                     _this.tickItems.splice(k, 1);
18267                     
18268                     if(typeof(e) == 'undefined' && view == false){
18269                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18270                     }
18271                     
18272                     rm = true;
18273                     return;
18274                 }
18275             });
18276             
18277             if(rm){
18278                 return;
18279             }
18280             
18281             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18282                 this.tickItems.push(r.data);
18283             }
18284             
18285             if(typeof(e) == 'undefined' && view == false){
18286                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18287             }
18288                     
18289             return;
18290         }
18291         
18292         if(r){
18293             this.onSelect(r, index);
18294         }
18295         if(doFocus !== false && !this.blockFocus){
18296             this.inputEl().focus();
18297         }
18298     },
18299
18300     // private
18301     restrictHeight : function(){
18302         //this.innerList.dom.style.height = '';
18303         //var inner = this.innerList.dom;
18304         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18305         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18306         //this.list.beginUpdate();
18307         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18308         this.list.alignTo(this.inputEl(), this.listAlign);
18309         this.list.alignTo(this.inputEl(), this.listAlign);
18310         //this.list.endUpdate();
18311     },
18312
18313     // private
18314     onEmptyResults : function(){
18315         
18316         if(this.tickable && this.editable){
18317             this.hasFocus = false;
18318             this.restrictHeight();
18319             return;
18320         }
18321         
18322         this.collapse();
18323     },
18324
18325     /**
18326      * Returns true if the dropdown list is expanded, else false.
18327      */
18328     isExpanded : function(){
18329         return this.list.isVisible();
18330     },
18331
18332     /**
18333      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18334      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18335      * @param {String} value The data value of the item to select
18336      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18337      * selected item if it is not currently in view (defaults to true)
18338      * @return {Boolean} True if the value matched an item in the list, else false
18339      */
18340     selectByValue : function(v, scrollIntoView){
18341         if(v !== undefined && v !== null){
18342             var r = this.findRecord(this.valueField || this.displayField, v);
18343             if(r){
18344                 this.select(this.store.indexOf(r), scrollIntoView);
18345                 return true;
18346             }
18347         }
18348         return false;
18349     },
18350
18351     /**
18352      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18353      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18354      * @param {Number} index The zero-based index of the list item to select
18355      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18356      * selected item if it is not currently in view (defaults to true)
18357      */
18358     select : function(index, scrollIntoView){
18359         this.selectedIndex = index;
18360         this.view.select(index);
18361         if(scrollIntoView !== false){
18362             var el = this.view.getNode(index);
18363             /*
18364              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18365              */
18366             if(el){
18367                 this.list.scrollChildIntoView(el, false);
18368             }
18369         }
18370     },
18371
18372     // private
18373     selectNext : function(){
18374         var ct = this.store.getCount();
18375         if(ct > 0){
18376             if(this.selectedIndex == -1){
18377                 this.select(0);
18378             }else if(this.selectedIndex < ct-1){
18379                 this.select(this.selectedIndex+1);
18380             }
18381         }
18382     },
18383
18384     // private
18385     selectPrev : function(){
18386         var ct = this.store.getCount();
18387         if(ct > 0){
18388             if(this.selectedIndex == -1){
18389                 this.select(0);
18390             }else if(this.selectedIndex != 0){
18391                 this.select(this.selectedIndex-1);
18392             }
18393         }
18394     },
18395
18396     // private
18397     onKeyUp : function(e){
18398         if(this.editable !== false && !e.isSpecialKey()){
18399             this.lastKey = e.getKey();
18400             this.dqTask.delay(this.queryDelay);
18401         }
18402     },
18403
18404     // private
18405     validateBlur : function(){
18406         return !this.list || !this.list.isVisible();   
18407     },
18408
18409     // private
18410     initQuery : function(){
18411         
18412         var v = this.getRawValue();
18413         
18414         if(this.tickable && this.editable){
18415             v = this.tickableInputEl().getValue();
18416         }
18417         
18418         this.doQuery(v);
18419     },
18420
18421     // private
18422     doForce : function(){
18423         if(this.inputEl().dom.value.length > 0){
18424             this.inputEl().dom.value =
18425                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18426              
18427         }
18428     },
18429
18430     /**
18431      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18432      * query allowing the query action to be canceled if needed.
18433      * @param {String} query The SQL query to execute
18434      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18435      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18436      * saved in the current store (defaults to false)
18437      */
18438     doQuery : function(q, forceAll){
18439         
18440         if(q === undefined || q === null){
18441             q = '';
18442         }
18443         var qe = {
18444             query: q,
18445             forceAll: forceAll,
18446             combo: this,
18447             cancel:false
18448         };
18449         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18450             return false;
18451         }
18452         q = qe.query;
18453         
18454         forceAll = qe.forceAll;
18455         if(forceAll === true || (q.length >= this.minChars)){
18456             
18457             this.hasQuery = true;
18458             
18459             if(this.lastQuery != q || this.alwaysQuery){
18460                 this.lastQuery = q;
18461                 if(this.mode == 'local'){
18462                     this.selectedIndex = -1;
18463                     if(forceAll){
18464                         this.store.clearFilter();
18465                     }else{
18466                         
18467                         if(this.specialFilter){
18468                             this.fireEvent('specialfilter', this);
18469                             this.onLoad();
18470                             return;
18471                         }
18472                         
18473                         this.store.filter(this.displayField, q);
18474                     }
18475                     
18476                     this.store.fireEvent("datachanged", this.store);
18477                     
18478                     this.onLoad();
18479                     
18480                     
18481                 }else{
18482                     
18483                     this.store.baseParams[this.queryParam] = q;
18484                     
18485                     var options = {params : this.getParams(q)};
18486                     
18487                     if(this.loadNext){
18488                         options.add = true;
18489                         options.params.start = this.page * this.pageSize;
18490                     }
18491                     
18492                     this.store.load(options);
18493                     
18494                     /*
18495                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18496                      *  we should expand the list on onLoad
18497                      *  so command out it
18498                      */
18499 //                    this.expand();
18500                 }
18501             }else{
18502                 this.selectedIndex = -1;
18503                 this.onLoad();   
18504             }
18505         }
18506         
18507         this.loadNext = false;
18508     },
18509     
18510     // private
18511     getParams : function(q){
18512         var p = {};
18513         //p[this.queryParam] = q;
18514         
18515         if(this.pageSize){
18516             p.start = 0;
18517             p.limit = this.pageSize;
18518         }
18519         return p;
18520     },
18521
18522     /**
18523      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18524      */
18525     collapse : function(){
18526         if(!this.isExpanded()){
18527             return;
18528         }
18529         
18530         this.list.hide();
18531         
18532         this.hasFocus = false;
18533         
18534         if(this.tickable){
18535             this.okBtn.hide();
18536             this.cancelBtn.hide();
18537             this.trigger.show();
18538             
18539             if(this.editable){
18540                 this.tickableInputEl().dom.value = '';
18541                 this.tickableInputEl().blur();
18542             }
18543             
18544         }
18545         
18546         Roo.get(document).un('mousedown', this.collapseIf, this);
18547         Roo.get(document).un('mousewheel', this.collapseIf, this);
18548         if (!this.editable) {
18549             Roo.get(document).un('keydown', this.listKeyPress, this);
18550         }
18551         this.fireEvent('collapse', this);
18552         
18553         this.validate();
18554     },
18555
18556     // private
18557     collapseIf : function(e){
18558         var in_combo  = e.within(this.el);
18559         var in_list =  e.within(this.list);
18560         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18561         
18562         if (in_combo || in_list || is_list) {
18563             //e.stopPropagation();
18564             return;
18565         }
18566         
18567         if(this.tickable){
18568             this.onTickableFooterButtonClick(e, false, false);
18569         }
18570
18571         this.collapse();
18572         
18573     },
18574
18575     /**
18576      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18577      */
18578     expand : function(){
18579        
18580         if(this.isExpanded() || !this.hasFocus){
18581             return;
18582         }
18583         
18584         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18585         this.list.setWidth(lw);
18586         
18587         Roo.log('expand');
18588         
18589         this.list.show();
18590         
18591         this.restrictHeight();
18592         
18593         if(this.tickable){
18594             
18595             this.tickItems = Roo.apply([], this.item);
18596             
18597             this.okBtn.show();
18598             this.cancelBtn.show();
18599             this.trigger.hide();
18600             
18601             if(this.editable){
18602                 this.tickableInputEl().focus();
18603             }
18604             
18605         }
18606         
18607         Roo.get(document).on('mousedown', this.collapseIf, this);
18608         Roo.get(document).on('mousewheel', this.collapseIf, this);
18609         if (!this.editable) {
18610             Roo.get(document).on('keydown', this.listKeyPress, this);
18611         }
18612         
18613         this.fireEvent('expand', this);
18614     },
18615
18616     // private
18617     // Implements the default empty TriggerField.onTriggerClick function
18618     onTriggerClick : function(e)
18619     {
18620         Roo.log('trigger click');
18621         
18622         if(this.disabled || !this.triggerList){
18623             return;
18624         }
18625         
18626         this.page = 0;
18627         this.loadNext = false;
18628         
18629         if(this.isExpanded()){
18630             this.collapse();
18631             if (!this.blockFocus) {
18632                 this.inputEl().focus();
18633             }
18634             
18635         }else {
18636             this.hasFocus = true;
18637             if(this.triggerAction == 'all') {
18638                 this.doQuery(this.allQuery, true);
18639             } else {
18640                 this.doQuery(this.getRawValue());
18641             }
18642             if (!this.blockFocus) {
18643                 this.inputEl().focus();
18644             }
18645         }
18646     },
18647     
18648     onTickableTriggerClick : function(e)
18649     {
18650         if(this.disabled){
18651             return;
18652         }
18653         
18654         this.page = 0;
18655         this.loadNext = false;
18656         this.hasFocus = true;
18657         
18658         if(this.triggerAction == 'all') {
18659             this.doQuery(this.allQuery, true);
18660         } else {
18661             this.doQuery(this.getRawValue());
18662         }
18663     },
18664     
18665     onSearchFieldClick : function(e)
18666     {
18667         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18668             this.onTickableFooterButtonClick(e, false, false);
18669             return;
18670         }
18671         
18672         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18673             return;
18674         }
18675         
18676         this.page = 0;
18677         this.loadNext = false;
18678         this.hasFocus = true;
18679         
18680         if(this.triggerAction == 'all') {
18681             this.doQuery(this.allQuery, true);
18682         } else {
18683             this.doQuery(this.getRawValue());
18684         }
18685     },
18686     
18687     listKeyPress : function(e)
18688     {
18689         //Roo.log('listkeypress');
18690         // scroll to first matching element based on key pres..
18691         if (e.isSpecialKey()) {
18692             return false;
18693         }
18694         var k = String.fromCharCode(e.getKey()).toUpperCase();
18695         //Roo.log(k);
18696         var match  = false;
18697         var csel = this.view.getSelectedNodes();
18698         var cselitem = false;
18699         if (csel.length) {
18700             var ix = this.view.indexOf(csel[0]);
18701             cselitem  = this.store.getAt(ix);
18702             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18703                 cselitem = false;
18704             }
18705             
18706         }
18707         
18708         this.store.each(function(v) { 
18709             if (cselitem) {
18710                 // start at existing selection.
18711                 if (cselitem.id == v.id) {
18712                     cselitem = false;
18713                 }
18714                 return true;
18715             }
18716                 
18717             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18718                 match = this.store.indexOf(v);
18719                 return false;
18720             }
18721             return true;
18722         }, this);
18723         
18724         if (match === false) {
18725             return true; // no more action?
18726         }
18727         // scroll to?
18728         this.view.select(match);
18729         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18730         sn.scrollIntoView(sn.dom.parentNode, false);
18731     },
18732     
18733     onViewScroll : function(e, t){
18734         
18735         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){
18736             return;
18737         }
18738         
18739         this.hasQuery = true;
18740         
18741         this.loading = this.list.select('.loading', true).first();
18742         
18743         if(this.loading === null){
18744             this.list.createChild({
18745                 tag: 'div',
18746                 cls: 'loading roo-select2-more-results roo-select2-active',
18747                 html: 'Loading more results...'
18748             });
18749             
18750             this.loading = this.list.select('.loading', true).first();
18751             
18752             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18753             
18754             this.loading.hide();
18755         }
18756         
18757         this.loading.show();
18758         
18759         var _combo = this;
18760         
18761         this.page++;
18762         this.loadNext = true;
18763         
18764         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18765         
18766         return;
18767     },
18768     
18769     addItem : function(o)
18770     {   
18771         var dv = ''; // display value
18772         
18773         if (this.displayField) {
18774             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18775         } else {
18776             // this is an error condition!!!
18777             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18778         }
18779         
18780         if(!dv.length){
18781             return;
18782         }
18783         
18784         var choice = this.choices.createChild({
18785             tag: 'li',
18786             cls: 'roo-select2-search-choice',
18787             cn: [
18788                 {
18789                     tag: 'div',
18790                     html: dv
18791                 },
18792                 {
18793                     tag: 'a',
18794                     href: '#',
18795                     cls: 'roo-select2-search-choice-close fa fa-times',
18796                     tabindex: '-1'
18797                 }
18798             ]
18799             
18800         }, this.searchField);
18801         
18802         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18803         
18804         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18805         
18806         this.item.push(o);
18807         
18808         this.lastData = o;
18809         
18810         this.syncValue();
18811         
18812         this.inputEl().dom.value = '';
18813         
18814         this.validate();
18815     },
18816     
18817     onRemoveItem : function(e, _self, o)
18818     {
18819         e.preventDefault();
18820         
18821         this.lastItem = Roo.apply([], this.item);
18822         
18823         var index = this.item.indexOf(o.data) * 1;
18824         
18825         if( index < 0){
18826             Roo.log('not this item?!');
18827             return;
18828         }
18829         
18830         this.item.splice(index, 1);
18831         o.item.remove();
18832         
18833         this.syncValue();
18834         
18835         this.fireEvent('remove', this, e);
18836         
18837         this.validate();
18838         
18839     },
18840     
18841     syncValue : function()
18842     {
18843         if(!this.item.length){
18844             this.clearValue();
18845             return;
18846         }
18847             
18848         var value = [];
18849         var _this = this;
18850         Roo.each(this.item, function(i){
18851             if(_this.valueField){
18852                 value.push(i[_this.valueField]);
18853                 return;
18854             }
18855
18856             value.push(i);
18857         });
18858
18859         this.value = value.join(',');
18860
18861         if(this.hiddenField){
18862             this.hiddenField.dom.value = this.value;
18863         }
18864         
18865         this.store.fireEvent("datachanged", this.store);
18866         
18867         this.validate();
18868     },
18869     
18870     clearItem : function()
18871     {
18872         if(!this.multiple){
18873             return;
18874         }
18875         
18876         this.item = [];
18877         
18878         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18879            c.remove();
18880         });
18881         
18882         this.syncValue();
18883         
18884         this.validate();
18885         
18886         if(this.tickable && !Roo.isTouch){
18887             this.view.refresh();
18888         }
18889     },
18890     
18891     inputEl: function ()
18892     {
18893         if(Roo.isIOS && this.useNativeIOS){
18894             return this.el.select('select.roo-ios-select', true).first();
18895         }
18896         
18897         if(Roo.isTouch && this.mobileTouchView){
18898             return this.el.select('input.form-control',true).first();
18899         }
18900         
18901         if(this.tickable){
18902             return this.searchField;
18903         }
18904         
18905         return this.el.select('input.form-control',true).first();
18906     },
18907     
18908     onTickableFooterButtonClick : function(e, btn, el)
18909     {
18910         e.preventDefault();
18911         
18912         this.lastItem = Roo.apply([], this.item);
18913         
18914         if(btn && btn.name == 'cancel'){
18915             this.tickItems = Roo.apply([], this.item);
18916             this.collapse();
18917             return;
18918         }
18919         
18920         this.clearItem();
18921         
18922         var _this = this;
18923         
18924         Roo.each(this.tickItems, function(o){
18925             _this.addItem(o);
18926         });
18927         
18928         this.collapse();
18929         
18930     },
18931     
18932     validate : function()
18933     {
18934         if(this.getVisibilityEl().hasClass('hidden')){
18935             return true;
18936         }
18937         
18938         var v = this.getRawValue();
18939         
18940         if(this.multiple){
18941             v = this.getValue();
18942         }
18943         
18944         if(this.disabled || this.allowBlank || v.length){
18945             this.markValid();
18946             return true;
18947         }
18948         
18949         this.markInvalid();
18950         return false;
18951     },
18952     
18953     tickableInputEl : function()
18954     {
18955         if(!this.tickable || !this.editable){
18956             return this.inputEl();
18957         }
18958         
18959         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18960     },
18961     
18962     
18963     getAutoCreateTouchView : function()
18964     {
18965         var id = Roo.id();
18966         
18967         var cfg = {
18968             cls: 'form-group' //input-group
18969         };
18970         
18971         var input =  {
18972             tag: 'input',
18973             id : id,
18974             type : this.inputType,
18975             cls : 'form-control x-combo-noedit',
18976             autocomplete: 'new-password',
18977             placeholder : this.placeholder || '',
18978             readonly : true
18979         };
18980         
18981         if (this.name) {
18982             input.name = this.name;
18983         }
18984         
18985         if (this.size) {
18986             input.cls += ' input-' + this.size;
18987         }
18988         
18989         if (this.disabled) {
18990             input.disabled = true;
18991         }
18992         
18993         var inputblock = {
18994             cls : 'roo-combobox-wrap',
18995             cn : [
18996                 input
18997             ]
18998         };
18999         
19000         if(this.before){
19001             inputblock.cls += ' input-group';
19002             
19003             inputblock.cn.unshift({
19004                 tag :'span',
19005                 cls : 'input-group-addon input-group-prepend input-group-text',
19006                 html : this.before
19007             });
19008         }
19009         
19010         if(this.removable && !this.multiple){
19011             inputblock.cls += ' roo-removable';
19012             
19013             inputblock.cn.push({
19014                 tag: 'button',
19015                 html : 'x',
19016                 cls : 'roo-combo-removable-btn close'
19017             });
19018         }
19019
19020         if(this.hasFeedback && !this.allowBlank){
19021             
19022             inputblock.cls += ' has-feedback';
19023             
19024             inputblock.cn.push({
19025                 tag: 'span',
19026                 cls: 'glyphicon form-control-feedback'
19027             });
19028             
19029         }
19030         
19031         if (this.after) {
19032             
19033             inputblock.cls += (this.before) ? '' : ' input-group';
19034             
19035             inputblock.cn.push({
19036                 tag :'span',
19037                 cls : 'input-group-addon input-group-append input-group-text',
19038                 html : this.after
19039             });
19040         }
19041
19042         
19043         var ibwrap = inputblock;
19044         
19045         if(this.multiple){
19046             ibwrap = {
19047                 tag: 'ul',
19048                 cls: 'roo-select2-choices',
19049                 cn:[
19050                     {
19051                         tag: 'li',
19052                         cls: 'roo-select2-search-field',
19053                         cn: [
19054
19055                             inputblock
19056                         ]
19057                     }
19058                 ]
19059             };
19060         
19061             
19062         }
19063         
19064         var combobox = {
19065             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19066             cn: [
19067                 {
19068                     tag: 'input',
19069                     type : 'hidden',
19070                     cls: 'form-hidden-field'
19071                 },
19072                 ibwrap
19073             ]
19074         };
19075         
19076         if(!this.multiple && this.showToggleBtn){
19077             
19078             var caret = {
19079                 cls: 'caret'
19080             };
19081             
19082             if (this.caret != false) {
19083                 caret = {
19084                      tag: 'i',
19085                      cls: 'fa fa-' + this.caret
19086                 };
19087                 
19088             }
19089             
19090             combobox.cn.push({
19091                 tag :'span',
19092                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19093                 cn : [
19094                     Roo.bootstrap.version == 3 ? caret : '',
19095                     {
19096                         tag: 'span',
19097                         cls: 'combobox-clear',
19098                         cn  : [
19099                             {
19100                                 tag : 'i',
19101                                 cls: 'icon-remove'
19102                             }
19103                         ]
19104                     }
19105                 ]
19106
19107             })
19108         }
19109         
19110         if(this.multiple){
19111             combobox.cls += ' roo-select2-container-multi';
19112         }
19113         
19114         var required =  this.allowBlank ?  {
19115                     tag : 'i',
19116                     style: 'display: none'
19117                 } : {
19118                    tag : 'i',
19119                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19120                    tooltip : 'This field is required'
19121                 };
19122         
19123         var align = this.labelAlign || this.parentLabelAlign();
19124         
19125         if (align ==='left' && this.fieldLabel.length) {
19126
19127             cfg.cn = [
19128                 required,
19129                 {
19130                     tag: 'label',
19131                     cls : 'control-label col-form-label',
19132                     html : this.fieldLabel
19133
19134                 },
19135                 {
19136                     cls : 'roo-combobox-wrap ', 
19137                     cn: [
19138                         combobox
19139                     ]
19140                 }
19141             ];
19142             
19143             var labelCfg = cfg.cn[1];
19144             var contentCfg = cfg.cn[2];
19145             
19146
19147             if(this.indicatorpos == 'right'){
19148                 cfg.cn = [
19149                     {
19150                         tag: 'label',
19151                         'for' :  id,
19152                         cls : 'control-label col-form-label',
19153                         cn : [
19154                             {
19155                                 tag : 'span',
19156                                 html : this.fieldLabel
19157                             },
19158                             required
19159                         ]
19160                     },
19161                     {
19162                         cls : "roo-combobox-wrap ",
19163                         cn: [
19164                             combobox
19165                         ]
19166                     }
19167
19168                 ];
19169                 
19170                 labelCfg = cfg.cn[0];
19171                 contentCfg = cfg.cn[1];
19172             }
19173             
19174            
19175             
19176             if(this.labelWidth > 12){
19177                 labelCfg.style = "width: " + this.labelWidth + 'px';
19178             }
19179            
19180             if(this.labelWidth < 13 && this.labelmd == 0){
19181                 this.labelmd = this.labelWidth;
19182             }
19183             
19184             if(this.labellg > 0){
19185                 labelCfg.cls += ' col-lg-' + this.labellg;
19186                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19187             }
19188             
19189             if(this.labelmd > 0){
19190                 labelCfg.cls += ' col-md-' + this.labelmd;
19191                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19192             }
19193             
19194             if(this.labelsm > 0){
19195                 labelCfg.cls += ' col-sm-' + this.labelsm;
19196                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19197             }
19198             
19199             if(this.labelxs > 0){
19200                 labelCfg.cls += ' col-xs-' + this.labelxs;
19201                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19202             }
19203                 
19204                 
19205         } else if ( this.fieldLabel.length) {
19206             cfg.cn = [
19207                required,
19208                 {
19209                     tag: 'label',
19210                     cls : 'control-label',
19211                     html : this.fieldLabel
19212
19213                 },
19214                 {
19215                     cls : '', 
19216                     cn: [
19217                         combobox
19218                     ]
19219                 }
19220             ];
19221             
19222             if(this.indicatorpos == 'right'){
19223                 cfg.cn = [
19224                     {
19225                         tag: 'label',
19226                         cls : 'control-label',
19227                         html : this.fieldLabel,
19228                         cn : [
19229                             required
19230                         ]
19231                     },
19232                     {
19233                         cls : '', 
19234                         cn: [
19235                             combobox
19236                         ]
19237                     }
19238                 ];
19239             }
19240         } else {
19241             cfg.cn = combobox;    
19242         }
19243         
19244         
19245         var settings = this;
19246         
19247         ['xs','sm','md','lg'].map(function(size){
19248             if (settings[size]) {
19249                 cfg.cls += ' col-' + size + '-' + settings[size];
19250             }
19251         });
19252         
19253         return cfg;
19254     },
19255     
19256     initTouchView : function()
19257     {
19258         this.renderTouchView();
19259         
19260         this.touchViewEl.on('scroll', function(){
19261             this.el.dom.scrollTop = 0;
19262         }, this);
19263         
19264         this.originalValue = this.getValue();
19265         
19266         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19267         
19268         this.inputEl().on("click", this.showTouchView, this);
19269         if (this.triggerEl) {
19270             this.triggerEl.on("click", this.showTouchView, this);
19271         }
19272         
19273         
19274         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19275         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19276         
19277         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19278         
19279         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19280         this.store.on('load', this.onTouchViewLoad, this);
19281         this.store.on('loadexception', this.onTouchViewLoadException, this);
19282         
19283         if(this.hiddenName){
19284             
19285             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19286             
19287             this.hiddenField.dom.value =
19288                 this.hiddenValue !== undefined ? this.hiddenValue :
19289                 this.value !== undefined ? this.value : '';
19290         
19291             this.el.dom.removeAttribute('name');
19292             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19293         }
19294         
19295         if(this.multiple){
19296             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19297             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19298         }
19299         
19300         if(this.removable && !this.multiple){
19301             var close = this.closeTriggerEl();
19302             if(close){
19303                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19304                 close.on('click', this.removeBtnClick, this, close);
19305             }
19306         }
19307         /*
19308          * fix the bug in Safari iOS8
19309          */
19310         this.inputEl().on("focus", function(e){
19311             document.activeElement.blur();
19312         }, this);
19313         
19314         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19315         
19316         return;
19317         
19318         
19319     },
19320     
19321     renderTouchView : function()
19322     {
19323         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19324         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19325         
19326         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19327         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19328         
19329         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19330         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19331         this.touchViewBodyEl.setStyle('overflow', 'auto');
19332         
19333         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19334         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19335         
19336         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19337         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19338         
19339     },
19340     
19341     showTouchView : function()
19342     {
19343         if(this.disabled){
19344             return;
19345         }
19346         
19347         this.touchViewHeaderEl.hide();
19348
19349         if(this.modalTitle.length){
19350             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19351             this.touchViewHeaderEl.show();
19352         }
19353
19354         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19355         this.touchViewEl.show();
19356
19357         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19358         
19359         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19360         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19361
19362         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19363
19364         if(this.modalTitle.length){
19365             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19366         }
19367         
19368         this.touchViewBodyEl.setHeight(bodyHeight);
19369
19370         if(this.animate){
19371             var _this = this;
19372             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19373         }else{
19374             this.touchViewEl.addClass(['in','show']);
19375         }
19376         
19377         if(this._touchViewMask){
19378             Roo.get(document.body).addClass("x-body-masked");
19379             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19380             this._touchViewMask.setStyle('z-index', 10000);
19381             this._touchViewMask.addClass('show');
19382         }
19383         
19384         this.doTouchViewQuery();
19385         
19386     },
19387     
19388     hideTouchView : function()
19389     {
19390         this.touchViewEl.removeClass(['in','show']);
19391
19392         if(this.animate){
19393             var _this = this;
19394             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19395         }else{
19396             this.touchViewEl.setStyle('display', 'none');
19397         }
19398         
19399         if(this._touchViewMask){
19400             this._touchViewMask.removeClass('show');
19401             Roo.get(document.body).removeClass("x-body-masked");
19402         }
19403     },
19404     
19405     setTouchViewValue : function()
19406     {
19407         if(this.multiple){
19408             this.clearItem();
19409         
19410             var _this = this;
19411
19412             Roo.each(this.tickItems, function(o){
19413                 this.addItem(o);
19414             }, this);
19415         }
19416         
19417         this.hideTouchView();
19418     },
19419     
19420     doTouchViewQuery : function()
19421     {
19422         var qe = {
19423             query: '',
19424             forceAll: true,
19425             combo: this,
19426             cancel:false
19427         };
19428         
19429         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19430             return false;
19431         }
19432         
19433         if(!this.alwaysQuery || this.mode == 'local'){
19434             this.onTouchViewLoad();
19435             return;
19436         }
19437         
19438         this.store.load();
19439     },
19440     
19441     onTouchViewBeforeLoad : function(combo,opts)
19442     {
19443         return;
19444     },
19445
19446     // private
19447     onTouchViewLoad : function()
19448     {
19449         if(this.store.getCount() < 1){
19450             this.onTouchViewEmptyResults();
19451             return;
19452         }
19453         
19454         this.clearTouchView();
19455         
19456         var rawValue = this.getRawValue();
19457         
19458         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19459         
19460         this.tickItems = [];
19461         
19462         this.store.data.each(function(d, rowIndex){
19463             var row = this.touchViewListGroup.createChild(template);
19464             
19465             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19466                 row.addClass(d.data.cls);
19467             }
19468             
19469             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19470                 var cfg = {
19471                     data : d.data,
19472                     html : d.data[this.displayField]
19473                 };
19474                 
19475                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19476                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19477                 }
19478             }
19479             row.removeClass('selected');
19480             if(!this.multiple && this.valueField &&
19481                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19482             {
19483                 // radio buttons..
19484                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19485                 row.addClass('selected');
19486             }
19487             
19488             if(this.multiple && this.valueField &&
19489                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19490             {
19491                 
19492                 // checkboxes...
19493                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19494                 this.tickItems.push(d.data);
19495             }
19496             
19497             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19498             
19499         }, this);
19500         
19501         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19502         
19503         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19504
19505         if(this.modalTitle.length){
19506             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19507         }
19508
19509         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19510         
19511         if(this.mobile_restrict_height && listHeight < bodyHeight){
19512             this.touchViewBodyEl.setHeight(listHeight);
19513         }
19514         
19515         var _this = this;
19516         
19517         if(firstChecked && listHeight > bodyHeight){
19518             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19519         }
19520         
19521     },
19522     
19523     onTouchViewLoadException : function()
19524     {
19525         this.hideTouchView();
19526     },
19527     
19528     onTouchViewEmptyResults : function()
19529     {
19530         this.clearTouchView();
19531         
19532         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19533         
19534         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19535         
19536     },
19537     
19538     clearTouchView : function()
19539     {
19540         this.touchViewListGroup.dom.innerHTML = '';
19541     },
19542     
19543     onTouchViewClick : function(e, el, o)
19544     {
19545         e.preventDefault();
19546         
19547         var row = o.row;
19548         var rowIndex = o.rowIndex;
19549         
19550         var r = this.store.getAt(rowIndex);
19551         
19552         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19553             
19554             if(!this.multiple){
19555                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19556                     c.dom.removeAttribute('checked');
19557                 }, this);
19558
19559                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19560
19561                 this.setFromData(r.data);
19562
19563                 var close = this.closeTriggerEl();
19564
19565                 if(close){
19566                     close.show();
19567                 }
19568
19569                 this.hideTouchView();
19570
19571                 this.fireEvent('select', this, r, rowIndex);
19572
19573                 return;
19574             }
19575
19576             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19577                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19578                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19579                 return;
19580             }
19581
19582             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19583             this.addItem(r.data);
19584             this.tickItems.push(r.data);
19585         }
19586     },
19587     
19588     getAutoCreateNativeIOS : function()
19589     {
19590         var cfg = {
19591             cls: 'form-group' //input-group,
19592         };
19593         
19594         var combobox =  {
19595             tag: 'select',
19596             cls : 'roo-ios-select'
19597         };
19598         
19599         if (this.name) {
19600             combobox.name = this.name;
19601         }
19602         
19603         if (this.disabled) {
19604             combobox.disabled = true;
19605         }
19606         
19607         var settings = this;
19608         
19609         ['xs','sm','md','lg'].map(function(size){
19610             if (settings[size]) {
19611                 cfg.cls += ' col-' + size + '-' + settings[size];
19612             }
19613         });
19614         
19615         cfg.cn = combobox;
19616         
19617         return cfg;
19618         
19619     },
19620     
19621     initIOSView : function()
19622     {
19623         this.store.on('load', this.onIOSViewLoad, this);
19624         
19625         return;
19626     },
19627     
19628     onIOSViewLoad : function()
19629     {
19630         if(this.store.getCount() < 1){
19631             return;
19632         }
19633         
19634         this.clearIOSView();
19635         
19636         if(this.allowBlank) {
19637             
19638             var default_text = '-- SELECT --';
19639             
19640             if(this.placeholder.length){
19641                 default_text = this.placeholder;
19642             }
19643             
19644             if(this.emptyTitle.length){
19645                 default_text += ' - ' + this.emptyTitle + ' -';
19646             }
19647             
19648             var opt = this.inputEl().createChild({
19649                 tag: 'option',
19650                 value : 0,
19651                 html : default_text
19652             });
19653             
19654             var o = {};
19655             o[this.valueField] = 0;
19656             o[this.displayField] = default_text;
19657             
19658             this.ios_options.push({
19659                 data : o,
19660                 el : opt
19661             });
19662             
19663         }
19664         
19665         this.store.data.each(function(d, rowIndex){
19666             
19667             var html = '';
19668             
19669             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19670                 html = d.data[this.displayField];
19671             }
19672             
19673             var value = '';
19674             
19675             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19676                 value = d.data[this.valueField];
19677             }
19678             
19679             var option = {
19680                 tag: 'option',
19681                 value : value,
19682                 html : html
19683             };
19684             
19685             if(this.value == d.data[this.valueField]){
19686                 option['selected'] = true;
19687             }
19688             
19689             var opt = this.inputEl().createChild(option);
19690             
19691             this.ios_options.push({
19692                 data : d.data,
19693                 el : opt
19694             });
19695             
19696         }, this);
19697         
19698         this.inputEl().on('change', function(){
19699            this.fireEvent('select', this);
19700         }, this);
19701         
19702     },
19703     
19704     clearIOSView: function()
19705     {
19706         this.inputEl().dom.innerHTML = '';
19707         
19708         this.ios_options = [];
19709     },
19710     
19711     setIOSValue: function(v)
19712     {
19713         this.value = v;
19714         
19715         if(!this.ios_options){
19716             return;
19717         }
19718         
19719         Roo.each(this.ios_options, function(opts){
19720            
19721            opts.el.dom.removeAttribute('selected');
19722            
19723            if(opts.data[this.valueField] != v){
19724                return;
19725            }
19726            
19727            opts.el.dom.setAttribute('selected', true);
19728            
19729         }, this);
19730     }
19731
19732     /** 
19733     * @cfg {Boolean} grow 
19734     * @hide 
19735     */
19736     /** 
19737     * @cfg {Number} growMin 
19738     * @hide 
19739     */
19740     /** 
19741     * @cfg {Number} growMax 
19742     * @hide 
19743     */
19744     /**
19745      * @hide
19746      * @method autoSize
19747      */
19748 });
19749
19750 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19751     
19752     header : {
19753         tag: 'div',
19754         cls: 'modal-header',
19755         cn: [
19756             {
19757                 tag: 'h4',
19758                 cls: 'modal-title'
19759             }
19760         ]
19761     },
19762     
19763     body : {
19764         tag: 'div',
19765         cls: 'modal-body',
19766         cn: [
19767             {
19768                 tag: 'ul',
19769                 cls: 'list-group'
19770             }
19771         ]
19772     },
19773     
19774     listItemRadio : {
19775         tag: 'li',
19776         cls: 'list-group-item',
19777         cn: [
19778             {
19779                 tag: 'span',
19780                 cls: 'roo-combobox-list-group-item-value'
19781             },
19782             {
19783                 tag: 'div',
19784                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19785                 cn: [
19786                     {
19787                         tag: 'input',
19788                         type: 'radio'
19789                     },
19790                     {
19791                         tag: 'label'
19792                     }
19793                 ]
19794             }
19795         ]
19796     },
19797     
19798     listItemCheckbox : {
19799         tag: 'li',
19800         cls: 'list-group-item',
19801         cn: [
19802             {
19803                 tag: 'span',
19804                 cls: 'roo-combobox-list-group-item-value'
19805             },
19806             {
19807                 tag: 'div',
19808                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19809                 cn: [
19810                     {
19811                         tag: 'input',
19812                         type: 'checkbox'
19813                     },
19814                     {
19815                         tag: 'label'
19816                     }
19817                 ]
19818             }
19819         ]
19820     },
19821     
19822     emptyResult : {
19823         tag: 'div',
19824         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19825     },
19826     
19827     footer : {
19828         tag: 'div',
19829         cls: 'modal-footer',
19830         cn: [
19831             {
19832                 tag: 'div',
19833                 cls: 'row',
19834                 cn: [
19835                     {
19836                         tag: 'div',
19837                         cls: 'col-xs-6 text-left',
19838                         cn: {
19839                             tag: 'button',
19840                             cls: 'btn btn-danger roo-touch-view-cancel',
19841                             html: 'Cancel'
19842                         }
19843                     },
19844                     {
19845                         tag: 'div',
19846                         cls: 'col-xs-6 text-right',
19847                         cn: {
19848                             tag: 'button',
19849                             cls: 'btn btn-success roo-touch-view-ok',
19850                             html: 'OK'
19851                         }
19852                     }
19853                 ]
19854             }
19855         ]
19856         
19857     }
19858 });
19859
19860 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19861     
19862     touchViewTemplate : {
19863         tag: 'div',
19864         cls: 'modal fade roo-combobox-touch-view',
19865         cn: [
19866             {
19867                 tag: 'div',
19868                 cls: 'modal-dialog',
19869                 style : 'position:fixed', // we have to fix position....
19870                 cn: [
19871                     {
19872                         tag: 'div',
19873                         cls: 'modal-content',
19874                         cn: [
19875                             Roo.bootstrap.form.ComboBox.header,
19876                             Roo.bootstrap.form.ComboBox.body,
19877                             Roo.bootstrap.form.ComboBox.footer
19878                         ]
19879                     }
19880                 ]
19881             }
19882         ]
19883     }
19884 });/*
19885  * Based on:
19886  * Ext JS Library 1.1.1
19887  * Copyright(c) 2006-2007, Ext JS, LLC.
19888  *
19889  * Originally Released Under LGPL - original licence link has changed is not relivant.
19890  *
19891  * Fork - LGPL
19892  * <script type="text/javascript">
19893  */
19894
19895 /**
19896  * @class Roo.View
19897  * @extends Roo.util.Observable
19898  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19899  * This class also supports single and multi selection modes. <br>
19900  * Create a data model bound view:
19901  <pre><code>
19902  var store = new Roo.data.Store(...);
19903
19904  var view = new Roo.View({
19905     el : "my-element",
19906     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19907  
19908     singleSelect: true,
19909     selectedClass: "ydataview-selected",
19910     store: store
19911  });
19912
19913  // listen for node click?
19914  view.on("click", function(vw, index, node, e){
19915  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19916  });
19917
19918  // load XML data
19919  dataModel.load("foobar.xml");
19920  </code></pre>
19921  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19922  * <br><br>
19923  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19924  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19925  * 
19926  * Note: old style constructor is still suported (container, template, config)
19927  * 
19928  * @constructor
19929  * Create a new View
19930  * @param {Object} config The config object
19931  * 
19932  */
19933 Roo.View = function(config, depreciated_tpl, depreciated_config){
19934     
19935     this.parent = false;
19936     
19937     if (typeof(depreciated_tpl) == 'undefined') {
19938         // new way.. - universal constructor.
19939         Roo.apply(this, config);
19940         this.el  = Roo.get(this.el);
19941     } else {
19942         // old format..
19943         this.el  = Roo.get(config);
19944         this.tpl = depreciated_tpl;
19945         Roo.apply(this, depreciated_config);
19946     }
19947     this.wrapEl  = this.el.wrap().wrap();
19948     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19949     
19950     
19951     if(typeof(this.tpl) == "string"){
19952         this.tpl = new Roo.Template(this.tpl);
19953     } else {
19954         // support xtype ctors..
19955         this.tpl = new Roo.factory(this.tpl, Roo);
19956     }
19957     
19958     
19959     this.tpl.compile();
19960     
19961     /** @private */
19962     this.addEvents({
19963         /**
19964          * @event beforeclick
19965          * Fires before a click is processed. Returns false to cancel the default action.
19966          * @param {Roo.View} this
19967          * @param {Number} index The index of the target node
19968          * @param {HTMLElement} node The target node
19969          * @param {Roo.EventObject} e The raw event object
19970          */
19971             "beforeclick" : true,
19972         /**
19973          * @event click
19974          * Fires when a template node is clicked.
19975          * @param {Roo.View} this
19976          * @param {Number} index The index of the target node
19977          * @param {HTMLElement} node The target node
19978          * @param {Roo.EventObject} e The raw event object
19979          */
19980             "click" : true,
19981         /**
19982          * @event dblclick
19983          * Fires when a template node is double clicked.
19984          * @param {Roo.View} this
19985          * @param {Number} index The index of the target node
19986          * @param {HTMLElement} node The target node
19987          * @param {Roo.EventObject} e The raw event object
19988          */
19989             "dblclick" : true,
19990         /**
19991          * @event contextmenu
19992          * Fires when a template node is right clicked.
19993          * @param {Roo.View} this
19994          * @param {Number} index The index of the target node
19995          * @param {HTMLElement} node The target node
19996          * @param {Roo.EventObject} e The raw event object
19997          */
19998             "contextmenu" : true,
19999         /**
20000          * @event selectionchange
20001          * Fires when the selected nodes change.
20002          * @param {Roo.View} this
20003          * @param {Array} selections Array of the selected nodes
20004          */
20005             "selectionchange" : true,
20006     
20007         /**
20008          * @event beforeselect
20009          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20010          * @param {Roo.View} this
20011          * @param {HTMLElement} node The node to be selected
20012          * @param {Array} selections Array of currently selected nodes
20013          */
20014             "beforeselect" : true,
20015         /**
20016          * @event preparedata
20017          * Fires on every row to render, to allow you to change the data.
20018          * @param {Roo.View} this
20019          * @param {Object} data to be rendered (change this)
20020          */
20021           "preparedata" : true
20022           
20023           
20024         });
20025
20026
20027
20028     this.el.on({
20029         "click": this.onClick,
20030         "dblclick": this.onDblClick,
20031         "contextmenu": this.onContextMenu,
20032         scope:this
20033     });
20034
20035     this.selections = [];
20036     this.nodes = [];
20037     this.cmp = new Roo.CompositeElementLite([]);
20038     if(this.store){
20039         this.store = Roo.factory(this.store, Roo.data);
20040         this.setStore(this.store, true);
20041     }
20042     
20043     if ( this.footer && this.footer.xtype) {
20044            
20045          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20046         
20047         this.footer.dataSource = this.store;
20048         this.footer.container = fctr;
20049         this.footer = Roo.factory(this.footer, Roo);
20050         fctr.insertFirst(this.el);
20051         
20052         // this is a bit insane - as the paging toolbar seems to detach the el..
20053 //        dom.parentNode.parentNode.parentNode
20054          // they get detached?
20055     }
20056     
20057     
20058     Roo.View.superclass.constructor.call(this);
20059     
20060     
20061 };
20062
20063 Roo.extend(Roo.View, Roo.util.Observable, {
20064     
20065      /**
20066      * @cfg {Roo.data.Store} store Data store to load data from.
20067      */
20068     store : false,
20069     
20070     /**
20071      * @cfg {String|Roo.Element} el The container element.
20072      */
20073     el : '',
20074     
20075     /**
20076      * @cfg {String|Roo.Template} tpl The template used by this View 
20077      */
20078     tpl : false,
20079     /**
20080      * @cfg {String} dataName the named area of the template to use as the data area
20081      *                          Works with domtemplates roo-name="name"
20082      */
20083     dataName: false,
20084     /**
20085      * @cfg {String} selectedClass The css class to add to selected nodes
20086      */
20087     selectedClass : "x-view-selected",
20088      /**
20089      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20090      */
20091     emptyText : "",
20092     
20093     /**
20094      * @cfg {String} text to display on mask (default Loading)
20095      */
20096     mask : false,
20097     /**
20098      * @cfg {Boolean} multiSelect Allow multiple selection
20099      */
20100     multiSelect : false,
20101     /**
20102      * @cfg {Boolean} singleSelect Allow single selection
20103      */
20104     singleSelect:  false,
20105     
20106     /**
20107      * @cfg {Boolean} toggleSelect - selecting 
20108      */
20109     toggleSelect : false,
20110     
20111     /**
20112      * @cfg {Boolean} tickable - selecting 
20113      */
20114     tickable : false,
20115     
20116     /**
20117      * Returns the element this view is bound to.
20118      * @return {Roo.Element}
20119      */
20120     getEl : function(){
20121         return this.wrapEl;
20122     },
20123     
20124     
20125
20126     /**
20127      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20128      */
20129     refresh : function(){
20130         //Roo.log('refresh');
20131         var t = this.tpl;
20132         
20133         // if we are using something like 'domtemplate', then
20134         // the what gets used is:
20135         // t.applySubtemplate(NAME, data, wrapping data..)
20136         // the outer template then get' applied with
20137         //     the store 'extra data'
20138         // and the body get's added to the
20139         //      roo-name="data" node?
20140         //      <span class='roo-tpl-{name}'></span> ?????
20141         
20142         
20143         
20144         this.clearSelections();
20145         this.el.update("");
20146         var html = [];
20147         var records = this.store.getRange();
20148         if(records.length < 1) {
20149             
20150             // is this valid??  = should it render a template??
20151             
20152             this.el.update(this.emptyText);
20153             return;
20154         }
20155         var el = this.el;
20156         if (this.dataName) {
20157             this.el.update(t.apply(this.store.meta)); //????
20158             el = this.el.child('.roo-tpl-' + this.dataName);
20159         }
20160         
20161         for(var i = 0, len = records.length; i < len; i++){
20162             var data = this.prepareData(records[i].data, i, records[i]);
20163             this.fireEvent("preparedata", this, data, i, records[i]);
20164             
20165             var d = Roo.apply({}, data);
20166             
20167             if(this.tickable){
20168                 Roo.apply(d, {'roo-id' : Roo.id()});
20169                 
20170                 var _this = this;
20171             
20172                 Roo.each(this.parent.item, function(item){
20173                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20174                         return;
20175                     }
20176                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20177                 });
20178             }
20179             
20180             html[html.length] = Roo.util.Format.trim(
20181                 this.dataName ?
20182                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20183                     t.apply(d)
20184             );
20185         }
20186         
20187         
20188         
20189         el.update(html.join(""));
20190         this.nodes = el.dom.childNodes;
20191         this.updateIndexes(0);
20192     },
20193     
20194
20195     /**
20196      * Function to override to reformat the data that is sent to
20197      * the template for each node.
20198      * DEPRICATED - use the preparedata event handler.
20199      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20200      * a JSON object for an UpdateManager bound view).
20201      */
20202     prepareData : function(data, index, record)
20203     {
20204         this.fireEvent("preparedata", this, data, index, record);
20205         return data;
20206     },
20207
20208     onUpdate : function(ds, record){
20209         // Roo.log('on update');   
20210         this.clearSelections();
20211         var index = this.store.indexOf(record);
20212         var n = this.nodes[index];
20213         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20214         n.parentNode.removeChild(n);
20215         this.updateIndexes(index, index);
20216     },
20217
20218     
20219     
20220 // --------- FIXME     
20221     onAdd : function(ds, records, index)
20222     {
20223         //Roo.log(['on Add', ds, records, index] );        
20224         this.clearSelections();
20225         if(this.nodes.length == 0){
20226             this.refresh();
20227             return;
20228         }
20229         var n = this.nodes[index];
20230         for(var i = 0, len = records.length; i < len; i++){
20231             var d = this.prepareData(records[i].data, i, records[i]);
20232             if(n){
20233                 this.tpl.insertBefore(n, d);
20234             }else{
20235                 
20236                 this.tpl.append(this.el, d);
20237             }
20238         }
20239         this.updateIndexes(index);
20240     },
20241
20242     onRemove : function(ds, record, index){
20243        // Roo.log('onRemove');
20244         this.clearSelections();
20245         var el = this.dataName  ?
20246             this.el.child('.roo-tpl-' + this.dataName) :
20247             this.el; 
20248         
20249         el.dom.removeChild(this.nodes[index]);
20250         this.updateIndexes(index);
20251     },
20252
20253     /**
20254      * Refresh an individual node.
20255      * @param {Number} index
20256      */
20257     refreshNode : function(index){
20258         this.onUpdate(this.store, this.store.getAt(index));
20259     },
20260
20261     updateIndexes : function(startIndex, endIndex){
20262         var ns = this.nodes;
20263         startIndex = startIndex || 0;
20264         endIndex = endIndex || ns.length - 1;
20265         for(var i = startIndex; i <= endIndex; i++){
20266             ns[i].nodeIndex = i;
20267         }
20268     },
20269
20270     /**
20271      * Changes the data store this view uses and refresh the view.
20272      * @param {Store} store
20273      */
20274     setStore : function(store, initial){
20275         if(!initial && this.store){
20276             this.store.un("datachanged", this.refresh);
20277             this.store.un("add", this.onAdd);
20278             this.store.un("remove", this.onRemove);
20279             this.store.un("update", this.onUpdate);
20280             this.store.un("clear", this.refresh);
20281             this.store.un("beforeload", this.onBeforeLoad);
20282             this.store.un("load", this.onLoad);
20283             this.store.un("loadexception", this.onLoad);
20284         }
20285         if(store){
20286           
20287             store.on("datachanged", this.refresh, this);
20288             store.on("add", this.onAdd, this);
20289             store.on("remove", this.onRemove, this);
20290             store.on("update", this.onUpdate, this);
20291             store.on("clear", this.refresh, this);
20292             store.on("beforeload", this.onBeforeLoad, this);
20293             store.on("load", this.onLoad, this);
20294             store.on("loadexception", this.onLoad, this);
20295         }
20296         
20297         if(store){
20298             this.refresh();
20299         }
20300     },
20301     /**
20302      * onbeforeLoad - masks the loading area.
20303      *
20304      */
20305     onBeforeLoad : function(store,opts)
20306     {
20307          //Roo.log('onBeforeLoad');   
20308         if (!opts.add) {
20309             this.el.update("");
20310         }
20311         this.el.mask(this.mask ? this.mask : "Loading" ); 
20312     },
20313     onLoad : function ()
20314     {
20315         this.el.unmask();
20316     },
20317     
20318
20319     /**
20320      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20321      * @param {HTMLElement} node
20322      * @return {HTMLElement} The template node
20323      */
20324     findItemFromChild : function(node){
20325         var el = this.dataName  ?
20326             this.el.child('.roo-tpl-' + this.dataName,true) :
20327             this.el.dom; 
20328         
20329         if(!node || node.parentNode == el){
20330                     return node;
20331             }
20332             var p = node.parentNode;
20333             while(p && p != el){
20334             if(p.parentNode == el){
20335                 return p;
20336             }
20337             p = p.parentNode;
20338         }
20339             return null;
20340     },
20341
20342     /** @ignore */
20343     onClick : function(e){
20344         var item = this.findItemFromChild(e.getTarget());
20345         if(item){
20346             var index = this.indexOf(item);
20347             if(this.onItemClick(item, index, e) !== false){
20348                 this.fireEvent("click", this, index, item, e);
20349             }
20350         }else{
20351             this.clearSelections();
20352         }
20353     },
20354
20355     /** @ignore */
20356     onContextMenu : function(e){
20357         var item = this.findItemFromChild(e.getTarget());
20358         if(item){
20359             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20360         }
20361     },
20362
20363     /** @ignore */
20364     onDblClick : function(e){
20365         var item = this.findItemFromChild(e.getTarget());
20366         if(item){
20367             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20368         }
20369     },
20370
20371     onItemClick : function(item, index, e)
20372     {
20373         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20374             return false;
20375         }
20376         if (this.toggleSelect) {
20377             var m = this.isSelected(item) ? 'unselect' : 'select';
20378             //Roo.log(m);
20379             var _t = this;
20380             _t[m](item, true, false);
20381             return true;
20382         }
20383         if(this.multiSelect || this.singleSelect){
20384             if(this.multiSelect && e.shiftKey && this.lastSelection){
20385                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20386             }else{
20387                 this.select(item, this.multiSelect && e.ctrlKey);
20388                 this.lastSelection = item;
20389             }
20390             
20391             if(!this.tickable){
20392                 e.preventDefault();
20393             }
20394             
20395         }
20396         return true;
20397     },
20398
20399     /**
20400      * Get the number of selected nodes.
20401      * @return {Number}
20402      */
20403     getSelectionCount : function(){
20404         return this.selections.length;
20405     },
20406
20407     /**
20408      * Get the currently selected nodes.
20409      * @return {Array} An array of HTMLElements
20410      */
20411     getSelectedNodes : function(){
20412         return this.selections;
20413     },
20414
20415     /**
20416      * Get the indexes of the selected nodes.
20417      * @return {Array}
20418      */
20419     getSelectedIndexes : function(){
20420         var indexes = [], s = this.selections;
20421         for(var i = 0, len = s.length; i < len; i++){
20422             indexes.push(s[i].nodeIndex);
20423         }
20424         return indexes;
20425     },
20426
20427     /**
20428      * Clear all selections
20429      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20430      */
20431     clearSelections : function(suppressEvent){
20432         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20433             this.cmp.elements = this.selections;
20434             this.cmp.removeClass(this.selectedClass);
20435             this.selections = [];
20436             if(!suppressEvent){
20437                 this.fireEvent("selectionchange", this, this.selections);
20438             }
20439         }
20440     },
20441
20442     /**
20443      * Returns true if the passed node is selected
20444      * @param {HTMLElement/Number} node The node or node index
20445      * @return {Boolean}
20446      */
20447     isSelected : function(node){
20448         var s = this.selections;
20449         if(s.length < 1){
20450             return false;
20451         }
20452         node = this.getNode(node);
20453         return s.indexOf(node) !== -1;
20454     },
20455
20456     /**
20457      * Selects nodes.
20458      * @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
20459      * @param {Boolean} keepExisting (optional) true to keep existing selections
20460      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20461      */
20462     select : function(nodeInfo, keepExisting, suppressEvent){
20463         if(nodeInfo instanceof Array){
20464             if(!keepExisting){
20465                 this.clearSelections(true);
20466             }
20467             for(var i = 0, len = nodeInfo.length; i < len; i++){
20468                 this.select(nodeInfo[i], true, true);
20469             }
20470             return;
20471         } 
20472         var node = this.getNode(nodeInfo);
20473         if(!node || this.isSelected(node)){
20474             return; // already selected.
20475         }
20476         if(!keepExisting){
20477             this.clearSelections(true);
20478         }
20479         
20480         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20481             Roo.fly(node).addClass(this.selectedClass);
20482             this.selections.push(node);
20483             if(!suppressEvent){
20484                 this.fireEvent("selectionchange", this, this.selections);
20485             }
20486         }
20487         
20488         
20489     },
20490       /**
20491      * Unselects nodes.
20492      * @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
20493      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20494      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20495      */
20496     unselect : function(nodeInfo, keepExisting, suppressEvent)
20497     {
20498         if(nodeInfo instanceof Array){
20499             Roo.each(this.selections, function(s) {
20500                 this.unselect(s, nodeInfo);
20501             }, this);
20502             return;
20503         }
20504         var node = this.getNode(nodeInfo);
20505         if(!node || !this.isSelected(node)){
20506             //Roo.log("not selected");
20507             return; // not selected.
20508         }
20509         // fireevent???
20510         var ns = [];
20511         Roo.each(this.selections, function(s) {
20512             if (s == node ) {
20513                 Roo.fly(node).removeClass(this.selectedClass);
20514
20515                 return;
20516             }
20517             ns.push(s);
20518         },this);
20519         
20520         this.selections= ns;
20521         this.fireEvent("selectionchange", this, this.selections);
20522     },
20523
20524     /**
20525      * Gets a template node.
20526      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20527      * @return {HTMLElement} The node or null if it wasn't found
20528      */
20529     getNode : function(nodeInfo){
20530         if(typeof nodeInfo == "string"){
20531             return document.getElementById(nodeInfo);
20532         }else if(typeof nodeInfo == "number"){
20533             return this.nodes[nodeInfo];
20534         }
20535         return nodeInfo;
20536     },
20537
20538     /**
20539      * Gets a range template nodes.
20540      * @param {Number} startIndex
20541      * @param {Number} endIndex
20542      * @return {Array} An array of nodes
20543      */
20544     getNodes : function(start, end){
20545         var ns = this.nodes;
20546         start = start || 0;
20547         end = typeof end == "undefined" ? ns.length - 1 : end;
20548         var nodes = [];
20549         if(start <= end){
20550             for(var i = start; i <= end; i++){
20551                 nodes.push(ns[i]);
20552             }
20553         } else{
20554             for(var i = start; i >= end; i--){
20555                 nodes.push(ns[i]);
20556             }
20557         }
20558         return nodes;
20559     },
20560
20561     /**
20562      * Finds the index of the passed node
20563      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20564      * @return {Number} The index of the node or -1
20565      */
20566     indexOf : function(node){
20567         node = this.getNode(node);
20568         if(typeof node.nodeIndex == "number"){
20569             return node.nodeIndex;
20570         }
20571         var ns = this.nodes;
20572         for(var i = 0, len = ns.length; i < len; i++){
20573             if(ns[i] == node){
20574                 return i;
20575             }
20576         }
20577         return -1;
20578     }
20579 });
20580 /*
20581  * - LGPL
20582  *
20583  * based on jquery fullcalendar
20584  * 
20585  */
20586
20587 Roo.bootstrap = Roo.bootstrap || {};
20588 /**
20589  * @class Roo.bootstrap.Calendar
20590  * @extends Roo.bootstrap.Component
20591  * Bootstrap Calendar class
20592  * @cfg {Boolean} loadMask (true|false) default false
20593  * @cfg {Object} header generate the user specific header of the calendar, default false
20594
20595  * @constructor
20596  * Create a new Container
20597  * @param {Object} config The config object
20598  */
20599
20600
20601
20602 Roo.bootstrap.Calendar = function(config){
20603     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20604      this.addEvents({
20605         /**
20606              * @event select
20607              * Fires when a date is selected
20608              * @param {DatePicker} this
20609              * @param {Date} date The selected date
20610              */
20611         'select': true,
20612         /**
20613              * @event monthchange
20614              * Fires when the displayed month changes 
20615              * @param {DatePicker} this
20616              * @param {Date} date The selected month
20617              */
20618         'monthchange': true,
20619         /**
20620              * @event evententer
20621              * Fires when mouse over an event
20622              * @param {Calendar} this
20623              * @param {event} Event
20624              */
20625         'evententer': true,
20626         /**
20627              * @event eventleave
20628              * Fires when the mouse leaves an
20629              * @param {Calendar} this
20630              * @param {event}
20631              */
20632         'eventleave': true,
20633         /**
20634              * @event eventclick
20635              * Fires when the mouse click an
20636              * @param {Calendar} this
20637              * @param {event}
20638              */
20639         'eventclick': true
20640         
20641     });
20642
20643 };
20644
20645 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20646     
20647           /**
20648      * @cfg {Roo.data.Store} store
20649      * The data source for the calendar
20650      */
20651         store : false,
20652      /**
20653      * @cfg {Number} startDay
20654      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20655      */
20656     startDay : 0,
20657     
20658     loadMask : false,
20659     
20660     header : false,
20661       
20662     getAutoCreate : function(){
20663         
20664         
20665         var fc_button = function(name, corner, style, content ) {
20666             return Roo.apply({},{
20667                 tag : 'span',
20668                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20669                          (corner.length ?
20670                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20671                             ''
20672                         ),
20673                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20674                 unselectable: 'on'
20675             });
20676         };
20677         
20678         var header = {};
20679         
20680         if(!this.header){
20681             header = {
20682                 tag : 'table',
20683                 cls : 'fc-header',
20684                 style : 'width:100%',
20685                 cn : [
20686                     {
20687                         tag: 'tr',
20688                         cn : [
20689                             {
20690                                 tag : 'td',
20691                                 cls : 'fc-header-left',
20692                                 cn : [
20693                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20694                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20695                                     { tag: 'span', cls: 'fc-header-space' },
20696                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20697
20698
20699                                 ]
20700                             },
20701
20702                             {
20703                                 tag : 'td',
20704                                 cls : 'fc-header-center',
20705                                 cn : [
20706                                     {
20707                                         tag: 'span',
20708                                         cls: 'fc-header-title',
20709                                         cn : {
20710                                             tag: 'H2',
20711                                             html : 'month / year'
20712                                         }
20713                                     }
20714
20715                                 ]
20716                             },
20717                             {
20718                                 tag : 'td',
20719                                 cls : 'fc-header-right',
20720                                 cn : [
20721                               /*      fc_button('month', 'left', '', 'month' ),
20722                                     fc_button('week', '', '', 'week' ),
20723                                     fc_button('day', 'right', '', 'day' )
20724                                 */    
20725
20726                                 ]
20727                             }
20728
20729                         ]
20730                     }
20731                 ]
20732             };
20733         }
20734         
20735         header = this.header;
20736         
20737        
20738         var cal_heads = function() {
20739             var ret = [];
20740             // fixme - handle this.
20741             
20742             for (var i =0; i < Date.dayNames.length; i++) {
20743                 var d = Date.dayNames[i];
20744                 ret.push({
20745                     tag: 'th',
20746                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20747                     html : d.substring(0,3)
20748                 });
20749                 
20750             }
20751             ret[0].cls += ' fc-first';
20752             ret[6].cls += ' fc-last';
20753             return ret;
20754         };
20755         var cal_cell = function(n) {
20756             return  {
20757                 tag: 'td',
20758                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20759                 cn : [
20760                     {
20761                         cn : [
20762                             {
20763                                 cls: 'fc-day-number',
20764                                 html: 'D'
20765                             },
20766                             {
20767                                 cls: 'fc-day-content',
20768                              
20769                                 cn : [
20770                                      {
20771                                         style: 'position: relative;' // height: 17px;
20772                                     }
20773                                 ]
20774                             }
20775                             
20776                             
20777                         ]
20778                     }
20779                 ]
20780                 
20781             }
20782         };
20783         var cal_rows = function() {
20784             
20785             var ret = [];
20786             for (var r = 0; r < 6; r++) {
20787                 var row= {
20788                     tag : 'tr',
20789                     cls : 'fc-week',
20790                     cn : []
20791                 };
20792                 
20793                 for (var i =0; i < Date.dayNames.length; i++) {
20794                     var d = Date.dayNames[i];
20795                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20796
20797                 }
20798                 row.cn[0].cls+=' fc-first';
20799                 row.cn[0].cn[0].style = 'min-height:90px';
20800                 row.cn[6].cls+=' fc-last';
20801                 ret.push(row);
20802                 
20803             }
20804             ret[0].cls += ' fc-first';
20805             ret[4].cls += ' fc-prev-last';
20806             ret[5].cls += ' fc-last';
20807             return ret;
20808             
20809         };
20810         
20811         var cal_table = {
20812             tag: 'table',
20813             cls: 'fc-border-separate',
20814             style : 'width:100%',
20815             cellspacing  : 0,
20816             cn : [
20817                 { 
20818                     tag: 'thead',
20819                     cn : [
20820                         { 
20821                             tag: 'tr',
20822                             cls : 'fc-first fc-last',
20823                             cn : cal_heads()
20824                         }
20825                     ]
20826                 },
20827                 { 
20828                     tag: 'tbody',
20829                     cn : cal_rows()
20830                 }
20831                   
20832             ]
20833         };
20834          
20835          var cfg = {
20836             cls : 'fc fc-ltr',
20837             cn : [
20838                 header,
20839                 {
20840                     cls : 'fc-content',
20841                     style : "position: relative;",
20842                     cn : [
20843                         {
20844                             cls : 'fc-view fc-view-month fc-grid',
20845                             style : 'position: relative',
20846                             unselectable : 'on',
20847                             cn : [
20848                                 {
20849                                     cls : 'fc-event-container',
20850                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20851                                 },
20852                                 cal_table
20853                             ]
20854                         }
20855                     ]
20856     
20857                 }
20858            ] 
20859             
20860         };
20861         
20862          
20863         
20864         return cfg;
20865     },
20866     
20867     
20868     initEvents : function()
20869     {
20870         if(!this.store){
20871             throw "can not find store for calendar";
20872         }
20873         
20874         var mark = {
20875             tag: "div",
20876             cls:"x-dlg-mask",
20877             style: "text-align:center",
20878             cn: [
20879                 {
20880                     tag: "div",
20881                     style: "background-color:white;width:50%;margin:250 auto",
20882                     cn: [
20883                         {
20884                             tag: "img",
20885                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20886                         },
20887                         {
20888                             tag: "span",
20889                             html: "Loading"
20890                         }
20891                         
20892                     ]
20893                 }
20894             ]
20895         };
20896         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20897         
20898         var size = this.el.select('.fc-content', true).first().getSize();
20899         this.maskEl.setSize(size.width, size.height);
20900         this.maskEl.enableDisplayMode("block");
20901         if(!this.loadMask){
20902             this.maskEl.hide();
20903         }
20904         
20905         this.store = Roo.factory(this.store, Roo.data);
20906         this.store.on('load', this.onLoad, this);
20907         this.store.on('beforeload', this.onBeforeLoad, this);
20908         
20909         this.resize();
20910         
20911         this.cells = this.el.select('.fc-day',true);
20912         //Roo.log(this.cells);
20913         this.textNodes = this.el.query('.fc-day-number');
20914         this.cells.addClassOnOver('fc-state-hover');
20915         
20916         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20917         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20918         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20919         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20920         
20921         this.on('monthchange', this.onMonthChange, this);
20922         
20923         this.update(new Date().clearTime());
20924     },
20925     
20926     resize : function() {
20927         var sz  = this.el.getSize();
20928         
20929         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20930         this.el.select('.fc-day-content div',true).setHeight(34);
20931     },
20932     
20933     
20934     // private
20935     showPrevMonth : function(e){
20936         this.update(this.activeDate.add("mo", -1));
20937     },
20938     showToday : function(e){
20939         this.update(new Date().clearTime());
20940     },
20941     // private
20942     showNextMonth : function(e){
20943         this.update(this.activeDate.add("mo", 1));
20944     },
20945
20946     // private
20947     showPrevYear : function(){
20948         this.update(this.activeDate.add("y", -1));
20949     },
20950
20951     // private
20952     showNextYear : function(){
20953         this.update(this.activeDate.add("y", 1));
20954     },
20955
20956     
20957    // private
20958     update : function(date)
20959     {
20960         var vd = this.activeDate;
20961         this.activeDate = date;
20962 //        if(vd && this.el){
20963 //            var t = date.getTime();
20964 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20965 //                Roo.log('using add remove');
20966 //                
20967 //                this.fireEvent('monthchange', this, date);
20968 //                
20969 //                this.cells.removeClass("fc-state-highlight");
20970 //                this.cells.each(function(c){
20971 //                   if(c.dateValue == t){
20972 //                       c.addClass("fc-state-highlight");
20973 //                       setTimeout(function(){
20974 //                            try{c.dom.firstChild.focus();}catch(e){}
20975 //                       }, 50);
20976 //                       return false;
20977 //                   }
20978 //                   return true;
20979 //                });
20980 //                return;
20981 //            }
20982 //        }
20983         
20984         var days = date.getDaysInMonth();
20985         
20986         var firstOfMonth = date.getFirstDateOfMonth();
20987         var startingPos = firstOfMonth.getDay()-this.startDay;
20988         
20989         if(startingPos < this.startDay){
20990             startingPos += 7;
20991         }
20992         
20993         var pm = date.add(Date.MONTH, -1);
20994         var prevStart = pm.getDaysInMonth()-startingPos;
20995 //        
20996         this.cells = this.el.select('.fc-day',true);
20997         this.textNodes = this.el.query('.fc-day-number');
20998         this.cells.addClassOnOver('fc-state-hover');
20999         
21000         var cells = this.cells.elements;
21001         var textEls = this.textNodes;
21002         
21003         Roo.each(cells, function(cell){
21004             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21005         });
21006         
21007         days += startingPos;
21008
21009         // convert everything to numbers so it's fast
21010         var day = 86400000;
21011         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21012         //Roo.log(d);
21013         //Roo.log(pm);
21014         //Roo.log(prevStart);
21015         
21016         var today = new Date().clearTime().getTime();
21017         var sel = date.clearTime().getTime();
21018         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21019         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21020         var ddMatch = this.disabledDatesRE;
21021         var ddText = this.disabledDatesText;
21022         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21023         var ddaysText = this.disabledDaysText;
21024         var format = this.format;
21025         
21026         var setCellClass = function(cal, cell){
21027             cell.row = 0;
21028             cell.events = [];
21029             cell.more = [];
21030             //Roo.log('set Cell Class');
21031             cell.title = "";
21032             var t = d.getTime();
21033             
21034             //Roo.log(d);
21035             
21036             cell.dateValue = t;
21037             if(t == today){
21038                 cell.className += " fc-today";
21039                 cell.className += " fc-state-highlight";
21040                 cell.title = cal.todayText;
21041             }
21042             if(t == sel){
21043                 // disable highlight in other month..
21044                 //cell.className += " fc-state-highlight";
21045                 
21046             }
21047             // disabling
21048             if(t < min) {
21049                 cell.className = " fc-state-disabled";
21050                 cell.title = cal.minText;
21051                 return;
21052             }
21053             if(t > max) {
21054                 cell.className = " fc-state-disabled";
21055                 cell.title = cal.maxText;
21056                 return;
21057             }
21058             if(ddays){
21059                 if(ddays.indexOf(d.getDay()) != -1){
21060                     cell.title = ddaysText;
21061                     cell.className = " fc-state-disabled";
21062                 }
21063             }
21064             if(ddMatch && format){
21065                 var fvalue = d.dateFormat(format);
21066                 if(ddMatch.test(fvalue)){
21067                     cell.title = ddText.replace("%0", fvalue);
21068                     cell.className = " fc-state-disabled";
21069                 }
21070             }
21071             
21072             if (!cell.initialClassName) {
21073                 cell.initialClassName = cell.dom.className;
21074             }
21075             
21076             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21077         };
21078
21079         var i = 0;
21080         
21081         for(; i < startingPos; i++) {
21082             textEls[i].innerHTML = (++prevStart);
21083             d.setDate(d.getDate()+1);
21084             
21085             cells[i].className = "fc-past fc-other-month";
21086             setCellClass(this, cells[i]);
21087         }
21088         
21089         var intDay = 0;
21090         
21091         for(; i < days; i++){
21092             intDay = i - startingPos + 1;
21093             textEls[i].innerHTML = (intDay);
21094             d.setDate(d.getDate()+1);
21095             
21096             cells[i].className = ''; // "x-date-active";
21097             setCellClass(this, cells[i]);
21098         }
21099         var extraDays = 0;
21100         
21101         for(; i < 42; i++) {
21102             textEls[i].innerHTML = (++extraDays);
21103             d.setDate(d.getDate()+1);
21104             
21105             cells[i].className = "fc-future fc-other-month";
21106             setCellClass(this, cells[i]);
21107         }
21108         
21109         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21110         
21111         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21112         
21113         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21114         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21115         
21116         if(totalRows != 6){
21117             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21118             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21119         }
21120         
21121         this.fireEvent('monthchange', this, date);
21122         
21123         
21124         /*
21125         if(!this.internalRender){
21126             var main = this.el.dom.firstChild;
21127             var w = main.offsetWidth;
21128             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21129             Roo.fly(main).setWidth(w);
21130             this.internalRender = true;
21131             // opera does not respect the auto grow header center column
21132             // then, after it gets a width opera refuses to recalculate
21133             // without a second pass
21134             if(Roo.isOpera && !this.secondPass){
21135                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21136                 this.secondPass = true;
21137                 this.update.defer(10, this, [date]);
21138             }
21139         }
21140         */
21141         
21142     },
21143     
21144     findCell : function(dt) {
21145         dt = dt.clearTime().getTime();
21146         var ret = false;
21147         this.cells.each(function(c){
21148             //Roo.log("check " +c.dateValue + '?=' + dt);
21149             if(c.dateValue == dt){
21150                 ret = c;
21151                 return false;
21152             }
21153             return true;
21154         });
21155         
21156         return ret;
21157     },
21158     
21159     findCells : function(ev) {
21160         var s = ev.start.clone().clearTime().getTime();
21161        // Roo.log(s);
21162         var e= ev.end.clone().clearTime().getTime();
21163        // Roo.log(e);
21164         var ret = [];
21165         this.cells.each(function(c){
21166              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21167             
21168             if(c.dateValue > e){
21169                 return ;
21170             }
21171             if(c.dateValue < s){
21172                 return ;
21173             }
21174             ret.push(c);
21175         });
21176         
21177         return ret;    
21178     },
21179     
21180 //    findBestRow: function(cells)
21181 //    {
21182 //        var ret = 0;
21183 //        
21184 //        for (var i =0 ; i < cells.length;i++) {
21185 //            ret  = Math.max(cells[i].rows || 0,ret);
21186 //        }
21187 //        return ret;
21188 //        
21189 //    },
21190     
21191     
21192     addItem : function(ev)
21193     {
21194         // look for vertical location slot in
21195         var cells = this.findCells(ev);
21196         
21197 //        ev.row = this.findBestRow(cells);
21198         
21199         // work out the location.
21200         
21201         var crow = false;
21202         var rows = [];
21203         for(var i =0; i < cells.length; i++) {
21204             
21205             cells[i].row = cells[0].row;
21206             
21207             if(i == 0){
21208                 cells[i].row = cells[i].row + 1;
21209             }
21210             
21211             if (!crow) {
21212                 crow = {
21213                     start : cells[i],
21214                     end :  cells[i]
21215                 };
21216                 continue;
21217             }
21218             if (crow.start.getY() == cells[i].getY()) {
21219                 // on same row.
21220                 crow.end = cells[i];
21221                 continue;
21222             }
21223             // different row.
21224             rows.push(crow);
21225             crow = {
21226                 start: cells[i],
21227                 end : cells[i]
21228             };
21229             
21230         }
21231         
21232         rows.push(crow);
21233         ev.els = [];
21234         ev.rows = rows;
21235         ev.cells = cells;
21236         
21237         cells[0].events.push(ev);
21238         
21239         this.calevents.push(ev);
21240     },
21241     
21242     clearEvents: function() {
21243         
21244         if(!this.calevents){
21245             return;
21246         }
21247         
21248         Roo.each(this.cells.elements, function(c){
21249             c.row = 0;
21250             c.events = [];
21251             c.more = [];
21252         });
21253         
21254         Roo.each(this.calevents, function(e) {
21255             Roo.each(e.els, function(el) {
21256                 el.un('mouseenter' ,this.onEventEnter, this);
21257                 el.un('mouseleave' ,this.onEventLeave, this);
21258                 el.remove();
21259             },this);
21260         },this);
21261         
21262         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21263             e.remove();
21264         });
21265         
21266     },
21267     
21268     renderEvents: function()
21269     {   
21270         var _this = this;
21271         
21272         this.cells.each(function(c) {
21273             
21274             if(c.row < 5){
21275                 return;
21276             }
21277             
21278             var ev = c.events;
21279             
21280             var r = 4;
21281             if(c.row != c.events.length){
21282                 r = 4 - (4 - (c.row - c.events.length));
21283             }
21284             
21285             c.events = ev.slice(0, r);
21286             c.more = ev.slice(r);
21287             
21288             if(c.more.length && c.more.length == 1){
21289                 c.events.push(c.more.pop());
21290             }
21291             
21292             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21293             
21294         });
21295             
21296         this.cells.each(function(c) {
21297             
21298             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21299             
21300             
21301             for (var e = 0; e < c.events.length; e++){
21302                 var ev = c.events[e];
21303                 var rows = ev.rows;
21304                 
21305                 for(var i = 0; i < rows.length; i++) {
21306                 
21307                     // how many rows should it span..
21308
21309                     var  cfg = {
21310                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21311                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21312
21313                         unselectable : "on",
21314                         cn : [
21315                             {
21316                                 cls: 'fc-event-inner',
21317                                 cn : [
21318     //                                {
21319     //                                  tag:'span',
21320     //                                  cls: 'fc-event-time',
21321     //                                  html : cells.length > 1 ? '' : ev.time
21322     //                                },
21323                                     {
21324                                       tag:'span',
21325                                       cls: 'fc-event-title',
21326                                       html : String.format('{0}', ev.title)
21327                                     }
21328
21329
21330                                 ]
21331                             },
21332                             {
21333                                 cls: 'ui-resizable-handle ui-resizable-e',
21334                                 html : '&nbsp;&nbsp;&nbsp'
21335                             }
21336
21337                         ]
21338                     };
21339
21340                     if (i == 0) {
21341                         cfg.cls += ' fc-event-start';
21342                     }
21343                     if ((i+1) == rows.length) {
21344                         cfg.cls += ' fc-event-end';
21345                     }
21346
21347                     var ctr = _this.el.select('.fc-event-container',true).first();
21348                     var cg = ctr.createChild(cfg);
21349
21350                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21351                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21352
21353                     var r = (c.more.length) ? 1 : 0;
21354                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21355                     cg.setWidth(ebox.right - sbox.x -2);
21356
21357                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21358                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21359                     cg.on('click', _this.onEventClick, _this, ev);
21360
21361                     ev.els.push(cg);
21362                     
21363                 }
21364                 
21365             }
21366             
21367             
21368             if(c.more.length){
21369                 var  cfg = {
21370                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21371                     style : 'position: absolute',
21372                     unselectable : "on",
21373                     cn : [
21374                         {
21375                             cls: 'fc-event-inner',
21376                             cn : [
21377                                 {
21378                                   tag:'span',
21379                                   cls: 'fc-event-title',
21380                                   html : 'More'
21381                                 }
21382
21383
21384                             ]
21385                         },
21386                         {
21387                             cls: 'ui-resizable-handle ui-resizable-e',
21388                             html : '&nbsp;&nbsp;&nbsp'
21389                         }
21390
21391                     ]
21392                 };
21393
21394                 var ctr = _this.el.select('.fc-event-container',true).first();
21395                 var cg = ctr.createChild(cfg);
21396
21397                 var sbox = c.select('.fc-day-content',true).first().getBox();
21398                 var ebox = c.select('.fc-day-content',true).first().getBox();
21399                 //Roo.log(cg);
21400                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21401                 cg.setWidth(ebox.right - sbox.x -2);
21402
21403                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21404                 
21405             }
21406             
21407         });
21408         
21409         
21410         
21411     },
21412     
21413     onEventEnter: function (e, el,event,d) {
21414         this.fireEvent('evententer', this, el, event);
21415     },
21416     
21417     onEventLeave: function (e, el,event,d) {
21418         this.fireEvent('eventleave', this, el, event);
21419     },
21420     
21421     onEventClick: function (e, el,event,d) {
21422         this.fireEvent('eventclick', this, el, event);
21423     },
21424     
21425     onMonthChange: function () {
21426         this.store.load();
21427     },
21428     
21429     onMoreEventClick: function(e, el, more)
21430     {
21431         var _this = this;
21432         
21433         this.calpopover.placement = 'right';
21434         this.calpopover.setTitle('More');
21435         
21436         this.calpopover.setContent('');
21437         
21438         var ctr = this.calpopover.el.select('.popover-content', true).first();
21439         
21440         Roo.each(more, function(m){
21441             var cfg = {
21442                 cls : 'fc-event-hori fc-event-draggable',
21443                 html : m.title
21444             };
21445             var cg = ctr.createChild(cfg);
21446             
21447             cg.on('click', _this.onEventClick, _this, m);
21448         });
21449         
21450         this.calpopover.show(el);
21451         
21452         
21453     },
21454     
21455     onLoad: function () 
21456     {   
21457         this.calevents = [];
21458         var cal = this;
21459         
21460         if(this.store.getCount() > 0){
21461             this.store.data.each(function(d){
21462                cal.addItem({
21463                     id : d.data.id,
21464                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21465                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21466                     time : d.data.start_time,
21467                     title : d.data.title,
21468                     description : d.data.description,
21469                     venue : d.data.venue
21470                 });
21471             });
21472         }
21473         
21474         this.renderEvents();
21475         
21476         if(this.calevents.length && this.loadMask){
21477             this.maskEl.hide();
21478         }
21479     },
21480     
21481     onBeforeLoad: function()
21482     {
21483         this.clearEvents();
21484         if(this.loadMask){
21485             this.maskEl.show();
21486         }
21487     }
21488 });
21489
21490  
21491  /*
21492  * - LGPL
21493  *
21494  * element
21495  * 
21496  */
21497
21498 /**
21499  * @class Roo.bootstrap.Popover
21500  * @extends Roo.bootstrap.Component
21501  * @parent none builder
21502  * @children Roo.bootstrap.Component
21503  * Bootstrap Popover class
21504  * @cfg {String} html contents of the popover   (or false to use children..)
21505  * @cfg {String} title of popover (or false to hide)
21506  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21507  * @cfg {String} trigger click || hover (or false to trigger manually)
21508  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21509  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21510  *      - if false and it has a 'parent' then it will be automatically added to that element
21511  *      - if string - Roo.get  will be called 
21512  * @cfg {Number} delay - delay before showing
21513  
21514  * @constructor
21515  * Create a new Popover
21516  * @param {Object} config The config object
21517  */
21518
21519 Roo.bootstrap.Popover = function(config){
21520     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21521     
21522     this.addEvents({
21523         // raw events
21524          /**
21525          * @event show
21526          * After the popover show
21527          * 
21528          * @param {Roo.bootstrap.Popover} this
21529          */
21530         "show" : true,
21531         /**
21532          * @event hide
21533          * After the popover hide
21534          * 
21535          * @param {Roo.bootstrap.Popover} this
21536          */
21537         "hide" : true
21538     });
21539 };
21540
21541 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21542     
21543     title: false,
21544     html: false,
21545     
21546     placement : 'right',
21547     trigger : 'hover', // hover
21548     modal : false,
21549     delay : 0,
21550     
21551     over: false,
21552     
21553     can_build_overlaid : false,
21554     
21555     maskEl : false, // the mask element
21556     headerEl : false,
21557     contentEl : false,
21558     alignEl : false, // when show is called with an element - this get's stored.
21559     
21560     getChildContainer : function()
21561     {
21562         return this.contentEl;
21563         
21564     },
21565     getPopoverHeader : function()
21566     {
21567         this.title = true; // flag not to hide it..
21568         this.headerEl.addClass('p-0');
21569         return this.headerEl
21570     },
21571     
21572     
21573     getAutoCreate : function(){
21574          
21575         var cfg = {
21576            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21577            style: 'display:block',
21578            cn : [
21579                 {
21580                     cls : 'arrow'
21581                 },
21582                 {
21583                     cls : 'popover-inner ',
21584                     cn : [
21585                         {
21586                             tag: 'h3',
21587                             cls: 'popover-title popover-header',
21588                             html : this.title === false ? '' : this.title
21589                         },
21590                         {
21591                             cls : 'popover-content popover-body '  + (this.cls || ''),
21592                             html : this.html || ''
21593                         }
21594                     ]
21595                     
21596                 }
21597            ]
21598         };
21599         
21600         return cfg;
21601     },
21602     /**
21603      * @param {string} the title
21604      */
21605     setTitle: function(str)
21606     {
21607         this.title = str;
21608         if (this.el) {
21609             this.headerEl.dom.innerHTML = str;
21610         }
21611         
21612     },
21613     /**
21614      * @param {string} the body content
21615      */
21616     setContent: function(str)
21617     {
21618         this.html = str;
21619         if (this.contentEl) {
21620             this.contentEl.dom.innerHTML = str;
21621         }
21622         
21623     },
21624     // as it get's added to the bottom of the page.
21625     onRender : function(ct, position)
21626     {
21627         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21628         
21629         
21630         
21631         if(!this.el){
21632             var cfg = Roo.apply({},  this.getAutoCreate());
21633             cfg.id = Roo.id();
21634             
21635             if (this.cls) {
21636                 cfg.cls += ' ' + this.cls;
21637             }
21638             if (this.style) {
21639                 cfg.style = this.style;
21640             }
21641             //Roo.log("adding to ");
21642             this.el = Roo.get(document.body).createChild(cfg, position);
21643 //            Roo.log(this.el);
21644         }
21645         
21646         this.contentEl = this.el.select('.popover-content',true).first();
21647         this.headerEl =  this.el.select('.popover-title',true).first();
21648         
21649         var nitems = [];
21650         if(typeof(this.items) != 'undefined'){
21651             var items = this.items;
21652             delete this.items;
21653
21654             for(var i =0;i < items.length;i++) {
21655                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21656             }
21657         }
21658
21659         this.items = nitems;
21660         
21661         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21662         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21663         
21664         
21665         
21666         this.initEvents();
21667     },
21668     
21669     resizeMask : function()
21670     {
21671         this.maskEl.setSize(
21672             Roo.lib.Dom.getViewWidth(true),
21673             Roo.lib.Dom.getViewHeight(true)
21674         );
21675     },
21676     
21677     initEvents : function()
21678     {
21679         
21680         if (!this.modal) { 
21681             Roo.bootstrap.Popover.register(this);
21682         }
21683          
21684         this.arrowEl = this.el.select('.arrow',true).first();
21685         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21686         this.el.enableDisplayMode('block');
21687         this.el.hide();
21688  
21689         
21690         if (this.over === false && !this.parent()) {
21691             return; 
21692         }
21693         if (this.triggers === false) {
21694             return;
21695         }
21696          
21697         // support parent
21698         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21699         var triggers = this.trigger ? this.trigger.split(' ') : [];
21700         Roo.each(triggers, function(trigger) {
21701         
21702             if (trigger == 'click') {
21703                 on_el.on('click', this.toggle, this);
21704             } else if (trigger != 'manual') {
21705                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21706                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21707       
21708                 on_el.on(eventIn  ,this.enter, this);
21709                 on_el.on(eventOut, this.leave, this);
21710             }
21711         }, this);
21712     },
21713     
21714     
21715     // private
21716     timeout : null,
21717     hoverState : null,
21718     
21719     toggle : function () {
21720         this.hoverState == 'in' ? this.leave() : this.enter();
21721     },
21722     
21723     enter : function () {
21724         
21725         clearTimeout(this.timeout);
21726     
21727         this.hoverState = 'in';
21728     
21729         if (!this.delay || !this.delay.show) {
21730             this.show();
21731             return;
21732         }
21733         var _t = this;
21734         this.timeout = setTimeout(function () {
21735             if (_t.hoverState == 'in') {
21736                 _t.show();
21737             }
21738         }, this.delay.show)
21739     },
21740     
21741     leave : function() {
21742         clearTimeout(this.timeout);
21743     
21744         this.hoverState = 'out';
21745     
21746         if (!this.delay || !this.delay.hide) {
21747             this.hide();
21748             return;
21749         }
21750         var _t = this;
21751         this.timeout = setTimeout(function () {
21752             if (_t.hoverState == 'out') {
21753                 _t.hide();
21754             }
21755         }, this.delay.hide)
21756     },
21757     
21758     /**
21759      * update the position of the dialog
21760      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21761      * 
21762      *
21763      */
21764     
21765     doAlign : function()
21766     {
21767         
21768         if (this.alignEl) {
21769             this.updatePosition(this.placement, true);
21770              
21771         } else {
21772             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21773             var es = this.el.getSize();
21774             var x = Roo.lib.Dom.getViewWidth()/2;
21775             var y = Roo.lib.Dom.getViewHeight()/2;
21776             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21777             
21778         }
21779
21780          
21781          
21782         
21783         
21784     },
21785     
21786     /**
21787      * Show the popover
21788      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21789      * @param {string} (left|right|top|bottom) position
21790      */
21791     show : function (on_el, placement)
21792     {
21793         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21794         on_el = on_el || false; // default to false
21795          
21796         if (!on_el) {
21797             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21798                 on_el = this.parent().el;
21799             } else if (this.over) {
21800                 on_el = Roo.get(this.over);
21801             }
21802             
21803         }
21804         
21805         this.alignEl = Roo.get( on_el );
21806
21807         if (!this.el) {
21808             this.render(document.body);
21809         }
21810         
21811         
21812          
21813         
21814         if (this.title === false) {
21815             this.headerEl.hide();
21816         }
21817         
21818        
21819         this.el.show();
21820         this.el.dom.style.display = 'block';
21821          
21822         this.doAlign();
21823         
21824         //var arrow = this.el.select('.arrow',true).first();
21825         //arrow.set(align[2], 
21826         
21827         this.el.addClass('in');
21828         
21829          
21830         
21831         this.hoverState = 'in';
21832         
21833         if (this.modal) {
21834             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21835             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21836             this.maskEl.dom.style.display = 'block';
21837             this.maskEl.addClass('show');
21838         }
21839         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21840  
21841         this.fireEvent('show', this);
21842         
21843     },
21844     /**
21845      * fire this manually after loading a grid in the table for example
21846      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21847      * @param {Boolean} try and move it if we cant get right position.
21848      */
21849     updatePosition : function(placement, try_move)
21850     {
21851         // allow for calling with no parameters
21852         placement = placement   ? placement :  this.placement;
21853         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21854         
21855         this.el.removeClass([
21856             'fade','top','bottom', 'left', 'right','in',
21857             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21858         ]);
21859         this.el.addClass(placement + ' bs-popover-' + placement);
21860         
21861         if (!this.alignEl ) {
21862             return false;
21863         }
21864         
21865         switch (placement) {
21866             case 'right':
21867                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21868                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21869                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21870                     //normal display... or moved up/down.
21871                     this.el.setXY(offset);
21872                     var xy = this.alignEl.getAnchorXY('tr', false);
21873                     xy[0]+=2;xy[1]+=5;
21874                     this.arrowEl.setXY(xy);
21875                     return true;
21876                 }
21877                 // continue through...
21878                 return this.updatePosition('left', false);
21879                 
21880             
21881             case 'left':
21882                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21883                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21884                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21885                     //normal display... or moved up/down.
21886                     this.el.setXY(offset);
21887                     var xy = this.alignEl.getAnchorXY('tl', false);
21888                     xy[0]-=10;xy[1]+=5; // << fix me
21889                     this.arrowEl.setXY(xy);
21890                     return true;
21891                 }
21892                 // call self...
21893                 return this.updatePosition('right', false);
21894             
21895             case 'top':
21896                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21897                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21898                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21899                     //normal display... or moved up/down.
21900                     this.el.setXY(offset);
21901                     var xy = this.alignEl.getAnchorXY('t', false);
21902                     xy[1]-=10; // << fix me
21903                     this.arrowEl.setXY(xy);
21904                     return true;
21905                 }
21906                 // fall through
21907                return this.updatePosition('bottom', false);
21908             
21909             case 'bottom':
21910                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21911                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21912                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21913                     //normal display... or moved up/down.
21914                     this.el.setXY(offset);
21915                     var xy = this.alignEl.getAnchorXY('b', false);
21916                      xy[1]+=2; // << fix me
21917                     this.arrowEl.setXY(xy);
21918                     return true;
21919                 }
21920                 // fall through
21921                 return this.updatePosition('top', false);
21922                 
21923             
21924         }
21925         
21926         
21927         return false;
21928     },
21929     
21930     hide : function()
21931     {
21932         this.el.setXY([0,0]);
21933         this.el.removeClass('in');
21934         this.el.hide();
21935         this.hoverState = null;
21936         this.maskEl.hide(); // always..
21937         this.fireEvent('hide', this);
21938     }
21939     
21940 });
21941
21942
21943 Roo.apply(Roo.bootstrap.Popover, {
21944
21945     alignment : {
21946         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21947         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21948         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21949         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21950     },
21951     
21952     zIndex : 20001,
21953
21954     clickHander : false,
21955     
21956     
21957
21958     onMouseDown : function(e)
21959     {
21960         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21961             /// what is nothing is showing..
21962             this.hideAll();
21963         }
21964          
21965     },
21966     
21967     
21968     popups : [],
21969     
21970     register : function(popup)
21971     {
21972         if (!Roo.bootstrap.Popover.clickHandler) {
21973             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21974         }
21975         // hide other popups.
21976         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21977         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21978         this.hideAll(); //<< why?
21979         //this.popups.push(popup);
21980     },
21981     hideAll : function()
21982     {
21983         this.popups.forEach(function(p) {
21984             p.hide();
21985         });
21986     },
21987     onShow : function() {
21988         Roo.bootstrap.Popover.popups.push(this);
21989     },
21990     onHide : function() {
21991         Roo.bootstrap.Popover.popups.remove(this);
21992     } 
21993
21994 });
21995 /**
21996  * @class Roo.bootstrap.PopoverNav
21997  * @extends Roo.bootstrap.nav.Simplebar
21998  * @parent Roo.bootstrap.Popover
21999  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22000  * @licence LGPL
22001  * Bootstrap Popover header navigation class
22002  * FIXME? should this go under nav?
22003  *
22004  * 
22005  * @constructor
22006  * Create a new Popover Header Navigation 
22007  * @param {Object} config The config object
22008  */
22009
22010 Roo.bootstrap.PopoverNav = function(config){
22011     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22012 };
22013
22014 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22015     
22016     
22017     container_method : 'getPopoverHeader' 
22018     
22019      
22020     
22021     
22022    
22023 });
22024
22025  
22026
22027  /*
22028  * - LGPL
22029  *
22030  * Progress
22031  * 
22032  */
22033
22034 /**
22035  * @class Roo.bootstrap.Progress
22036  * @extends Roo.bootstrap.Component
22037  * @children Roo.bootstrap.ProgressBar
22038  * Bootstrap Progress class
22039  * @cfg {Boolean} striped striped of the progress bar
22040  * @cfg {Boolean} active animated of the progress bar
22041  * 
22042  * 
22043  * @constructor
22044  * Create a new Progress
22045  * @param {Object} config The config object
22046  */
22047
22048 Roo.bootstrap.Progress = function(config){
22049     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22050 };
22051
22052 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22053     
22054     striped : false,
22055     active: false,
22056     
22057     getAutoCreate : function(){
22058         var cfg = {
22059             tag: 'div',
22060             cls: 'progress'
22061         };
22062         
22063         
22064         if(this.striped){
22065             cfg.cls += ' progress-striped';
22066         }
22067       
22068         if(this.active){
22069             cfg.cls += ' active';
22070         }
22071         
22072         
22073         return cfg;
22074     }
22075    
22076 });
22077
22078  
22079
22080  /*
22081  * - LGPL
22082  *
22083  * ProgressBar
22084  * 
22085  */
22086
22087 /**
22088  * @class Roo.bootstrap.ProgressBar
22089  * @extends Roo.bootstrap.Component
22090  * Bootstrap ProgressBar class
22091  * @cfg {Number} aria_valuenow aria-value now
22092  * @cfg {Number} aria_valuemin aria-value min
22093  * @cfg {Number} aria_valuemax aria-value max
22094  * @cfg {String} label label for the progress bar
22095  * @cfg {String} panel (success | info | warning | danger )
22096  * @cfg {String} role role of the progress bar
22097  * @cfg {String} sr_only text
22098  * 
22099  * 
22100  * @constructor
22101  * Create a new ProgressBar
22102  * @param {Object} config The config object
22103  */
22104
22105 Roo.bootstrap.ProgressBar = function(config){
22106     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22107 };
22108
22109 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22110     
22111     aria_valuenow : 0,
22112     aria_valuemin : 0,
22113     aria_valuemax : 100,
22114     label : false,
22115     panel : false,
22116     role : false,
22117     sr_only: false,
22118     
22119     getAutoCreate : function()
22120     {
22121         
22122         var cfg = {
22123             tag: 'div',
22124             cls: 'progress-bar',
22125             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22126         };
22127         
22128         if(this.sr_only){
22129             cfg.cn = {
22130                 tag: 'span',
22131                 cls: 'sr-only',
22132                 html: this.sr_only
22133             }
22134         }
22135         
22136         if(this.role){
22137             cfg.role = this.role;
22138         }
22139         
22140         if(this.aria_valuenow){
22141             cfg['aria-valuenow'] = this.aria_valuenow;
22142         }
22143         
22144         if(this.aria_valuemin){
22145             cfg['aria-valuemin'] = this.aria_valuemin;
22146         }
22147         
22148         if(this.aria_valuemax){
22149             cfg['aria-valuemax'] = this.aria_valuemax;
22150         }
22151         
22152         if(this.label && !this.sr_only){
22153             cfg.html = this.label;
22154         }
22155         
22156         if(this.panel){
22157             cfg.cls += ' progress-bar-' + this.panel;
22158         }
22159         
22160         return cfg;
22161     },
22162     
22163     update : function(aria_valuenow)
22164     {
22165         this.aria_valuenow = aria_valuenow;
22166         
22167         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22168     }
22169    
22170 });
22171
22172  
22173
22174  /**
22175  * @class Roo.bootstrap.TabGroup
22176  * @extends Roo.bootstrap.Column
22177  * @children Roo.bootstrap.TabPanel
22178  * Bootstrap Column class
22179  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22180  * @cfg {Boolean} carousel true to make the group behave like a carousel
22181  * @cfg {Boolean} bullets show bullets for the panels
22182  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22183  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22184  * @cfg {Boolean} showarrow (true|false) show arrow default true
22185  * 
22186  * @constructor
22187  * Create a new TabGroup
22188  * @param {Object} config The config object
22189  */
22190
22191 Roo.bootstrap.TabGroup = function(config){
22192     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22193     if (!this.navId) {
22194         this.navId = Roo.id();
22195     }
22196     this.tabs = [];
22197     Roo.bootstrap.TabGroup.register(this);
22198     
22199 };
22200
22201 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22202     
22203     carousel : false,
22204     transition : false,
22205     bullets : 0,
22206     timer : 0,
22207     autoslide : false,
22208     slideFn : false,
22209     slideOnTouch : false,
22210     showarrow : true,
22211     
22212     getAutoCreate : function()
22213     {
22214         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22215         
22216         cfg.cls += ' tab-content';
22217         
22218         if (this.carousel) {
22219             cfg.cls += ' carousel slide';
22220             
22221             cfg.cn = [{
22222                cls : 'carousel-inner',
22223                cn : []
22224             }];
22225         
22226             if(this.bullets  && !Roo.isTouch){
22227                 
22228                 var bullets = {
22229                     cls : 'carousel-bullets',
22230                     cn : []
22231                 };
22232                
22233                 if(this.bullets_cls){
22234                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22235                 }
22236                 
22237                 bullets.cn.push({
22238                     cls : 'clear'
22239                 });
22240                 
22241                 cfg.cn[0].cn.push(bullets);
22242             }
22243             
22244             if(this.showarrow){
22245                 cfg.cn[0].cn.push({
22246                     tag : 'div',
22247                     class : 'carousel-arrow',
22248                     cn : [
22249                         {
22250                             tag : 'div',
22251                             class : 'carousel-prev',
22252                             cn : [
22253                                 {
22254                                     tag : 'i',
22255                                     class : 'fa fa-chevron-left'
22256                                 }
22257                             ]
22258                         },
22259                         {
22260                             tag : 'div',
22261                             class : 'carousel-next',
22262                             cn : [
22263                                 {
22264                                     tag : 'i',
22265                                     class : 'fa fa-chevron-right'
22266                                 }
22267                             ]
22268                         }
22269                     ]
22270                 });
22271             }
22272             
22273         }
22274         
22275         return cfg;
22276     },
22277     
22278     initEvents:  function()
22279     {
22280 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22281 //            this.el.on("touchstart", this.onTouchStart, this);
22282 //        }
22283         
22284         if(this.autoslide){
22285             var _this = this;
22286             
22287             this.slideFn = window.setInterval(function() {
22288                 _this.showPanelNext();
22289             }, this.timer);
22290         }
22291         
22292         if(this.showarrow){
22293             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22294             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22295         }
22296         
22297         
22298     },
22299     
22300 //    onTouchStart : function(e, el, o)
22301 //    {
22302 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22303 //            return;
22304 //        }
22305 //        
22306 //        this.showPanelNext();
22307 //    },
22308     
22309     
22310     getChildContainer : function()
22311     {
22312         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22313     },
22314     
22315     /**
22316     * register a Navigation item
22317     * @param {Roo.bootstrap.nav.Item} the navitem to add
22318     */
22319     register : function(item)
22320     {
22321         this.tabs.push( item);
22322         item.navId = this.navId; // not really needed..
22323         this.addBullet();
22324     
22325     },
22326     
22327     getActivePanel : function()
22328     {
22329         var r = false;
22330         Roo.each(this.tabs, function(t) {
22331             if (t.active) {
22332                 r = t;
22333                 return false;
22334             }
22335             return null;
22336         });
22337         return r;
22338         
22339     },
22340     getPanelByName : function(n)
22341     {
22342         var r = false;
22343         Roo.each(this.tabs, function(t) {
22344             if (t.tabId == n) {
22345                 r = t;
22346                 return false;
22347             }
22348             return null;
22349         });
22350         return r;
22351     },
22352     indexOfPanel : function(p)
22353     {
22354         var r = false;
22355         Roo.each(this.tabs, function(t,i) {
22356             if (t.tabId == p.tabId) {
22357                 r = i;
22358                 return false;
22359             }
22360             return null;
22361         });
22362         return r;
22363     },
22364     /**
22365      * show a specific panel
22366      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22367      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22368      */
22369     showPanel : function (pan)
22370     {
22371         if(this.transition || typeof(pan) == 'undefined'){
22372             Roo.log("waiting for the transitionend");
22373             return false;
22374         }
22375         
22376         if (typeof(pan) == 'number') {
22377             pan = this.tabs[pan];
22378         }
22379         
22380         if (typeof(pan) == 'string') {
22381             pan = this.getPanelByName(pan);
22382         }
22383         
22384         var cur = this.getActivePanel();
22385         
22386         if(!pan || !cur){
22387             Roo.log('pan or acitve pan is undefined');
22388             return false;
22389         }
22390         
22391         if (pan.tabId == this.getActivePanel().tabId) {
22392             return true;
22393         }
22394         
22395         if (false === cur.fireEvent('beforedeactivate')) {
22396             return false;
22397         }
22398         
22399         if(this.bullets > 0 && !Roo.isTouch){
22400             this.setActiveBullet(this.indexOfPanel(pan));
22401         }
22402         
22403         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22404             
22405             //class="carousel-item carousel-item-next carousel-item-left"
22406             
22407             this.transition = true;
22408             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22409             var lr = dir == 'next' ? 'left' : 'right';
22410             pan.el.addClass(dir); // or prev
22411             pan.el.addClass('carousel-item-' + dir); // or prev
22412             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22413             cur.el.addClass(lr); // or right
22414             pan.el.addClass(lr);
22415             cur.el.addClass('carousel-item-' +lr); // or right
22416             pan.el.addClass('carousel-item-' +lr);
22417             
22418             
22419             var _this = this;
22420             cur.el.on('transitionend', function() {
22421                 Roo.log("trans end?");
22422                 
22423                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22424                 pan.setActive(true);
22425                 
22426                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22427                 cur.setActive(false);
22428                 
22429                 _this.transition = false;
22430                 
22431             }, this, { single:  true } );
22432             
22433             return true;
22434         }
22435         
22436         cur.setActive(false);
22437         pan.setActive(true);
22438         
22439         return true;
22440         
22441     },
22442     showPanelNext : function()
22443     {
22444         var i = this.indexOfPanel(this.getActivePanel());
22445         
22446         if (i >= this.tabs.length - 1 && !this.autoslide) {
22447             return;
22448         }
22449         
22450         if (i >= this.tabs.length - 1 && this.autoslide) {
22451             i = -1;
22452         }
22453         
22454         this.showPanel(this.tabs[i+1]);
22455     },
22456     
22457     showPanelPrev : function()
22458     {
22459         var i = this.indexOfPanel(this.getActivePanel());
22460         
22461         if (i  < 1 && !this.autoslide) {
22462             return;
22463         }
22464         
22465         if (i < 1 && this.autoslide) {
22466             i = this.tabs.length;
22467         }
22468         
22469         this.showPanel(this.tabs[i-1]);
22470     },
22471     
22472     
22473     addBullet: function()
22474     {
22475         if(!this.bullets || Roo.isTouch){
22476             return;
22477         }
22478         var ctr = this.el.select('.carousel-bullets',true).first();
22479         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22480         var bullet = ctr.createChild({
22481             cls : 'bullet bullet-' + i
22482         },ctr.dom.lastChild);
22483         
22484         
22485         var _this = this;
22486         
22487         bullet.on('click', (function(e, el, o, ii, t){
22488
22489             e.preventDefault();
22490
22491             this.showPanel(ii);
22492
22493             if(this.autoslide && this.slideFn){
22494                 clearInterval(this.slideFn);
22495                 this.slideFn = window.setInterval(function() {
22496                     _this.showPanelNext();
22497                 }, this.timer);
22498             }
22499
22500         }).createDelegate(this, [i, bullet], true));
22501                 
22502         
22503     },
22504      
22505     setActiveBullet : function(i)
22506     {
22507         if(Roo.isTouch){
22508             return;
22509         }
22510         
22511         Roo.each(this.el.select('.bullet', true).elements, function(el){
22512             el.removeClass('selected');
22513         });
22514
22515         var bullet = this.el.select('.bullet-' + i, true).first();
22516         
22517         if(!bullet){
22518             return;
22519         }
22520         
22521         bullet.addClass('selected');
22522     }
22523     
22524     
22525   
22526 });
22527
22528  
22529
22530  
22531  
22532 Roo.apply(Roo.bootstrap.TabGroup, {
22533     
22534     groups: {},
22535      /**
22536     * register a Navigation Group
22537     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22538     */
22539     register : function(navgrp)
22540     {
22541         this.groups[navgrp.navId] = navgrp;
22542         
22543     },
22544     /**
22545     * fetch a Navigation Group based on the navigation ID
22546     * if one does not exist , it will get created.
22547     * @param {string} the navgroup to add
22548     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22549     */
22550     get: function(navId) {
22551         if (typeof(this.groups[navId]) == 'undefined') {
22552             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22553         }
22554         return this.groups[navId] ;
22555     }
22556     
22557     
22558     
22559 });
22560
22561  /*
22562  * - LGPL
22563  *
22564  * TabPanel
22565  * 
22566  */
22567
22568 /**
22569  * @class Roo.bootstrap.TabPanel
22570  * @extends Roo.bootstrap.Component
22571  * @children Roo.bootstrap.Component
22572  * Bootstrap TabPanel class
22573  * @cfg {Boolean} active panel active
22574  * @cfg {String} html panel content
22575  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22576  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22577  * @cfg {String} href click to link..
22578  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22579  * 
22580  * 
22581  * @constructor
22582  * Create a new TabPanel
22583  * @param {Object} config The config object
22584  */
22585
22586 Roo.bootstrap.TabPanel = function(config){
22587     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22588     this.addEvents({
22589         /**
22590              * @event changed
22591              * Fires when the active status changes
22592              * @param {Roo.bootstrap.TabPanel} this
22593              * @param {Boolean} state the new state
22594             
22595          */
22596         'changed': true,
22597         /**
22598              * @event beforedeactivate
22599              * Fires before a tab is de-activated - can be used to do validation on a form.
22600              * @param {Roo.bootstrap.TabPanel} this
22601              * @return {Boolean} false if there is an error
22602             
22603          */
22604         'beforedeactivate': true
22605      });
22606     
22607     this.tabId = this.tabId || Roo.id();
22608   
22609 };
22610
22611 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22612     
22613     active: false,
22614     html: false,
22615     tabId: false,
22616     navId : false,
22617     href : '',
22618     touchSlide : false,
22619     getAutoCreate : function(){
22620         
22621         
22622         var cfg = {
22623             tag: 'div',
22624             // item is needed for carousel - not sure if it has any effect otherwise
22625             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22626             html: this.html || ''
22627         };
22628         
22629         if(this.active){
22630             cfg.cls += ' active';
22631         }
22632         
22633         if(this.tabId){
22634             cfg.tabId = this.tabId;
22635         }
22636         
22637         
22638         
22639         return cfg;
22640     },
22641     
22642     initEvents:  function()
22643     {
22644         var p = this.parent();
22645         
22646         this.navId = this.navId || p.navId;
22647         
22648         if (typeof(this.navId) != 'undefined') {
22649             // not really needed.. but just in case.. parent should be a NavGroup.
22650             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22651             
22652             tg.register(this);
22653             
22654             var i = tg.tabs.length - 1;
22655             
22656             if(this.active && tg.bullets > 0 && i < tg.bullets){
22657                 tg.setActiveBullet(i);
22658             }
22659         }
22660         
22661         this.el.on('click', this.onClick, this);
22662         
22663         if(Roo.isTouch && this.touchSlide){
22664             this.el.on("touchstart", this.onTouchStart, this);
22665             this.el.on("touchmove", this.onTouchMove, this);
22666             this.el.on("touchend", this.onTouchEnd, this);
22667         }
22668         
22669     },
22670     
22671     onRender : function(ct, position)
22672     {
22673         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22674     },
22675     
22676     setActive : function(state)
22677     {
22678         Roo.log("panel - set active " + this.tabId + "=" + state);
22679         
22680         this.active = state;
22681         if (!state) {
22682             this.el.removeClass('active');
22683             
22684         } else  if (!this.el.hasClass('active')) {
22685             this.el.addClass('active');
22686         }
22687         
22688         this.fireEvent('changed', this, state);
22689     },
22690     
22691     onClick : function(e)
22692     {
22693         e.preventDefault();
22694         
22695         if(!this.href.length){
22696             return;
22697         }
22698         
22699         window.location.href = this.href;
22700     },
22701     
22702     startX : 0,
22703     startY : 0,
22704     endX : 0,
22705     endY : 0,
22706     swiping : false,
22707     
22708     onTouchStart : function(e)
22709     {
22710         this.swiping = false;
22711         
22712         this.startX = e.browserEvent.touches[0].clientX;
22713         this.startY = e.browserEvent.touches[0].clientY;
22714     },
22715     
22716     onTouchMove : function(e)
22717     {
22718         this.swiping = true;
22719         
22720         this.endX = e.browserEvent.touches[0].clientX;
22721         this.endY = e.browserEvent.touches[0].clientY;
22722     },
22723     
22724     onTouchEnd : function(e)
22725     {
22726         if(!this.swiping){
22727             this.onClick(e);
22728             return;
22729         }
22730         
22731         var tabGroup = this.parent();
22732         
22733         if(this.endX > this.startX){ // swiping right
22734             tabGroup.showPanelPrev();
22735             return;
22736         }
22737         
22738         if(this.startX > this.endX){ // swiping left
22739             tabGroup.showPanelNext();
22740             return;
22741         }
22742     }
22743     
22744     
22745 });
22746  
22747
22748  
22749
22750  /*
22751  * - LGPL
22752  *
22753  * DateField
22754  * 
22755  */
22756
22757 /**
22758  * @class Roo.bootstrap.form.DateField
22759  * @extends Roo.bootstrap.form.Input
22760  * Bootstrap DateField class
22761  * @cfg {Number} weekStart default 0
22762  * @cfg {String} viewMode default empty, (months|years)
22763  * @cfg {String} minViewMode default empty, (months|years)
22764  * @cfg {Number} startDate default -Infinity
22765  * @cfg {Number} endDate default Infinity
22766  * @cfg {Boolean} todayHighlight default false
22767  * @cfg {Boolean} todayBtn default false
22768  * @cfg {Boolean} calendarWeeks default false
22769  * @cfg {Object} daysOfWeekDisabled default empty
22770  * @cfg {Boolean} singleMode default false (true | false)
22771  * 
22772  * @cfg {Boolean} keyboardNavigation default true
22773  * @cfg {String} language default en
22774  * 
22775  * @constructor
22776  * Create a new DateField
22777  * @param {Object} config The config object
22778  */
22779
22780 Roo.bootstrap.form.DateField = function(config){
22781     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22782      this.addEvents({
22783             /**
22784              * @event show
22785              * Fires when this field show.
22786              * @param {Roo.bootstrap.form.DateField} this
22787              * @param {Mixed} date The date value
22788              */
22789             show : true,
22790             /**
22791              * @event show
22792              * Fires when this field hide.
22793              * @param {Roo.bootstrap.form.DateField} this
22794              * @param {Mixed} date The date value
22795              */
22796             hide : true,
22797             /**
22798              * @event select
22799              * Fires when select a date.
22800              * @param {Roo.bootstrap.form.DateField} this
22801              * @param {Mixed} date The date value
22802              */
22803             select : true,
22804             /**
22805              * @event beforeselect
22806              * Fires when before select a date.
22807              * @param {Roo.bootstrap.form.DateField} this
22808              * @param {Mixed} date The date value
22809              */
22810             beforeselect : true
22811         });
22812 };
22813
22814 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22815     
22816     /**
22817      * @cfg {String} format
22818      * The default date format string which can be overriden for localization support.  The format must be
22819      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22820      */
22821     format : "m/d/y",
22822     /**
22823      * @cfg {String} altFormats
22824      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22825      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22826      */
22827     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22828     
22829     weekStart : 0,
22830     
22831     viewMode : '',
22832     
22833     minViewMode : '',
22834     
22835     todayHighlight : false,
22836     
22837     todayBtn: false,
22838     
22839     language: 'en',
22840     
22841     keyboardNavigation: true,
22842     
22843     calendarWeeks: false,
22844     
22845     startDate: -Infinity,
22846     
22847     endDate: Infinity,
22848     
22849     daysOfWeekDisabled: [],
22850     
22851     _events: [],
22852     
22853     singleMode : false,
22854     
22855     UTCDate: function()
22856     {
22857         return new Date(Date.UTC.apply(Date, arguments));
22858     },
22859     
22860     UTCToday: function()
22861     {
22862         var today = new Date();
22863         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22864     },
22865     
22866     getDate: function() {
22867             var d = this.getUTCDate();
22868             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22869     },
22870     
22871     getUTCDate: function() {
22872             return this.date;
22873     },
22874     
22875     setDate: function(d) {
22876             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22877     },
22878     
22879     setUTCDate: function(d) {
22880             this.date = d;
22881             this.setValue(this.formatDate(this.date));
22882     },
22883         
22884     onRender: function(ct, position)
22885     {
22886         
22887         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22888         
22889         this.language = this.language || 'en';
22890         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22891         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22892         
22893         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22894         this.format = this.format || 'm/d/y';
22895         this.isInline = false;
22896         this.isInput = true;
22897         this.component = this.el.select('.add-on', true).first() || false;
22898         this.component = (this.component && this.component.length === 0) ? false : this.component;
22899         this.hasInput = this.component && this.inputEl().length;
22900         
22901         if (typeof(this.minViewMode === 'string')) {
22902             switch (this.minViewMode) {
22903                 case 'months':
22904                     this.minViewMode = 1;
22905                     break;
22906                 case 'years':
22907                     this.minViewMode = 2;
22908                     break;
22909                 default:
22910                     this.minViewMode = 0;
22911                     break;
22912             }
22913         }
22914         
22915         if (typeof(this.viewMode === 'string')) {
22916             switch (this.viewMode) {
22917                 case 'months':
22918                     this.viewMode = 1;
22919                     break;
22920                 case 'years':
22921                     this.viewMode = 2;
22922                     break;
22923                 default:
22924                     this.viewMode = 0;
22925                     break;
22926             }
22927         }
22928                 
22929         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22930         
22931 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22932         
22933         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22934         
22935         this.picker().on('mousedown', this.onMousedown, this);
22936         this.picker().on('click', this.onClick, this);
22937         
22938         this.picker().addClass('datepicker-dropdown');
22939         
22940         this.startViewMode = this.viewMode;
22941         
22942         if(this.singleMode){
22943             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22944                 v.setVisibilityMode(Roo.Element.DISPLAY);
22945                 v.hide();
22946             });
22947             
22948             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22949                 v.setStyle('width', '189px');
22950             });
22951         }
22952         
22953         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22954             if(!this.calendarWeeks){
22955                 v.remove();
22956                 return;
22957             }
22958             
22959             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22960             v.attr('colspan', function(i, val){
22961                 return parseInt(val) + 1;
22962             });
22963         });
22964                         
22965         
22966         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22967         
22968         this.setStartDate(this.startDate);
22969         this.setEndDate(this.endDate);
22970         
22971         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22972         
22973         this.fillDow();
22974         this.fillMonths();
22975         this.update();
22976         this.showMode();
22977         
22978         if(this.isInline) {
22979             this.showPopup();
22980         }
22981     },
22982     
22983     picker : function()
22984     {
22985         return this.pickerEl;
22986 //        return this.el.select('.datepicker', true).first();
22987     },
22988     
22989     fillDow: function()
22990     {
22991         var dowCnt = this.weekStart;
22992         
22993         var dow = {
22994             tag: 'tr',
22995             cn: [
22996                 
22997             ]
22998         };
22999         
23000         if(this.calendarWeeks){
23001             dow.cn.push({
23002                 tag: 'th',
23003                 cls: 'cw',
23004                 html: '&nbsp;'
23005             })
23006         }
23007         
23008         while (dowCnt < this.weekStart + 7) {
23009             dow.cn.push({
23010                 tag: 'th',
23011                 cls: 'dow',
23012                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23013             });
23014         }
23015         
23016         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23017     },
23018     
23019     fillMonths: function()
23020     {    
23021         var i = 0;
23022         var months = this.picker().select('>.datepicker-months td', true).first();
23023         
23024         months.dom.innerHTML = '';
23025         
23026         while (i < 12) {
23027             var month = {
23028                 tag: 'span',
23029                 cls: 'month',
23030                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23031             };
23032             
23033             months.createChild(month);
23034         }
23035         
23036     },
23037     
23038     update: function()
23039     {
23040         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;
23041         
23042         if (this.date < this.startDate) {
23043             this.viewDate = new Date(this.startDate);
23044         } else if (this.date > this.endDate) {
23045             this.viewDate = new Date(this.endDate);
23046         } else {
23047             this.viewDate = new Date(this.date);
23048         }
23049         
23050         this.fill();
23051     },
23052     
23053     fill: function() 
23054     {
23055         var d = new Date(this.viewDate),
23056                 year = d.getUTCFullYear(),
23057                 month = d.getUTCMonth(),
23058                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23059                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23060                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23061                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23062                 currentDate = this.date && this.date.valueOf(),
23063                 today = this.UTCToday();
23064         
23065         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23066         
23067 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23068         
23069 //        this.picker.select('>tfoot th.today').
23070 //                                              .text(dates[this.language].today)
23071 //                                              .toggle(this.todayBtn !== false);
23072     
23073         this.updateNavArrows();
23074         this.fillMonths();
23075                                                 
23076         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23077         
23078         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23079          
23080         prevMonth.setUTCDate(day);
23081         
23082         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23083         
23084         var nextMonth = new Date(prevMonth);
23085         
23086         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23087         
23088         nextMonth = nextMonth.valueOf();
23089         
23090         var fillMonths = false;
23091         
23092         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23093         
23094         while(prevMonth.valueOf() <= nextMonth) {
23095             var clsName = '';
23096             
23097             if (prevMonth.getUTCDay() === this.weekStart) {
23098                 if(fillMonths){
23099                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23100                 }
23101                     
23102                 fillMonths = {
23103                     tag: 'tr',
23104                     cn: []
23105                 };
23106                 
23107                 if(this.calendarWeeks){
23108                     // ISO 8601: First week contains first thursday.
23109                     // ISO also states week starts on Monday, but we can be more abstract here.
23110                     var
23111                     // Start of current week: based on weekstart/current date
23112                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23113                     // Thursday of this week
23114                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23115                     // First Thursday of year, year from thursday
23116                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23117                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23118                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23119                     
23120                     fillMonths.cn.push({
23121                         tag: 'td',
23122                         cls: 'cw',
23123                         html: calWeek
23124                     });
23125                 }
23126             }
23127             
23128             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23129                 clsName += ' old';
23130             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23131                 clsName += ' new';
23132             }
23133             if (this.todayHighlight &&
23134                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23135                 prevMonth.getUTCMonth() == today.getMonth() &&
23136                 prevMonth.getUTCDate() == today.getDate()) {
23137                 clsName += ' today';
23138             }
23139             
23140             if (currentDate && prevMonth.valueOf() === currentDate) {
23141                 clsName += ' active';
23142             }
23143             
23144             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23145                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23146                     clsName += ' disabled';
23147             }
23148             
23149             fillMonths.cn.push({
23150                 tag: 'td',
23151                 cls: 'day ' + clsName,
23152                 html: prevMonth.getDate()
23153             });
23154             
23155             prevMonth.setDate(prevMonth.getDate()+1);
23156         }
23157           
23158         var currentYear = this.date && this.date.getUTCFullYear();
23159         var currentMonth = this.date && this.date.getUTCMonth();
23160         
23161         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23162         
23163         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23164             v.removeClass('active');
23165             
23166             if(currentYear === year && k === currentMonth){
23167                 v.addClass('active');
23168             }
23169             
23170             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23171                 v.addClass('disabled');
23172             }
23173             
23174         });
23175         
23176         
23177         year = parseInt(year/10, 10) * 10;
23178         
23179         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23180         
23181         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23182         
23183         year -= 1;
23184         for (var i = -1; i < 11; i++) {
23185             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23186                 tag: 'span',
23187                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23188                 html: year
23189             });
23190             
23191             year += 1;
23192         }
23193     },
23194     
23195     showMode: function(dir) 
23196     {
23197         if (dir) {
23198             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23199         }
23200         
23201         Roo.each(this.picker().select('>div',true).elements, function(v){
23202             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23203             v.hide();
23204         });
23205         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23206     },
23207     
23208     place: function()
23209     {
23210         if(this.isInline) {
23211             return;
23212         }
23213         
23214         this.picker().removeClass(['bottom', 'top']);
23215         
23216         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23217             /*
23218              * place to the top of element!
23219              *
23220              */
23221             
23222             this.picker().addClass('top');
23223             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23224             
23225             return;
23226         }
23227         
23228         this.picker().addClass('bottom');
23229         
23230         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23231     },
23232     
23233     parseDate : function(value)
23234     {
23235         if(!value || value instanceof Date){
23236             return value;
23237         }
23238         var v = Date.parseDate(value, this.format);
23239         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23240             v = Date.parseDate(value, 'Y-m-d');
23241         }
23242         if(!v && this.altFormats){
23243             if(!this.altFormatsArray){
23244                 this.altFormatsArray = this.altFormats.split("|");
23245             }
23246             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23247                 v = Date.parseDate(value, this.altFormatsArray[i]);
23248             }
23249         }
23250         return v;
23251     },
23252     
23253     formatDate : function(date, fmt)
23254     {   
23255         return (!date || !(date instanceof Date)) ?
23256         date : date.dateFormat(fmt || this.format);
23257     },
23258     
23259     onFocus : function()
23260     {
23261         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23262         this.showPopup();
23263     },
23264     
23265     onBlur : function()
23266     {
23267         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23268         
23269         var d = this.inputEl().getValue();
23270         
23271         this.setValue(d);
23272                 
23273         this.hidePopup();
23274     },
23275     
23276     showPopup : function()
23277     {
23278         this.picker().show();
23279         this.update();
23280         this.place();
23281         
23282         this.fireEvent('showpopup', this, this.date);
23283     },
23284     
23285     hidePopup : function()
23286     {
23287         if(this.isInline) {
23288             return;
23289         }
23290         this.picker().hide();
23291         this.viewMode = this.startViewMode;
23292         this.showMode();
23293         
23294         this.fireEvent('hidepopup', this, this.date);
23295         
23296     },
23297     
23298     onMousedown: function(e)
23299     {
23300         e.stopPropagation();
23301         e.preventDefault();
23302     },
23303     
23304     keyup: function(e)
23305     {
23306         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23307         this.update();
23308     },
23309
23310     setValue: function(v)
23311     {
23312         if(this.fireEvent('beforeselect', this, v) !== false){
23313             var d = new Date(this.parseDate(v) ).clearTime();
23314         
23315             if(isNaN(d.getTime())){
23316                 this.date = this.viewDate = '';
23317                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23318                 return;
23319             }
23320
23321             v = this.formatDate(d);
23322
23323             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23324
23325             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23326
23327             this.update();
23328
23329             this.fireEvent('select', this, this.date);
23330         }
23331     },
23332     
23333     getValue: function()
23334     {
23335         return this.formatDate(this.date);
23336     },
23337     
23338     fireKey: function(e)
23339     {
23340         if (!this.picker().isVisible()){
23341             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23342                 this.showPopup();
23343             }
23344             return;
23345         }
23346         
23347         var dateChanged = false,
23348         dir, day, month,
23349         newDate, newViewDate;
23350         
23351         switch(e.keyCode){
23352             case 27: // escape
23353                 this.hidePopup();
23354                 e.preventDefault();
23355                 break;
23356             case 37: // left
23357             case 39: // right
23358                 if (!this.keyboardNavigation) {
23359                     break;
23360                 }
23361                 dir = e.keyCode == 37 ? -1 : 1;
23362                 
23363                 if (e.ctrlKey){
23364                     newDate = this.moveYear(this.date, dir);
23365                     newViewDate = this.moveYear(this.viewDate, dir);
23366                 } else if (e.shiftKey){
23367                     newDate = this.moveMonth(this.date, dir);
23368                     newViewDate = this.moveMonth(this.viewDate, dir);
23369                 } else {
23370                     newDate = new Date(this.date);
23371                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23372                     newViewDate = new Date(this.viewDate);
23373                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23374                 }
23375                 if (this.dateWithinRange(newDate)){
23376                     this.date = newDate;
23377                     this.viewDate = newViewDate;
23378                     this.setValue(this.formatDate(this.date));
23379 //                    this.update();
23380                     e.preventDefault();
23381                     dateChanged = true;
23382                 }
23383                 break;
23384             case 38: // up
23385             case 40: // down
23386                 if (!this.keyboardNavigation) {
23387                     break;
23388                 }
23389                 dir = e.keyCode == 38 ? -1 : 1;
23390                 if (e.ctrlKey){
23391                     newDate = this.moveYear(this.date, dir);
23392                     newViewDate = this.moveYear(this.viewDate, dir);
23393                 } else if (e.shiftKey){
23394                     newDate = this.moveMonth(this.date, dir);
23395                     newViewDate = this.moveMonth(this.viewDate, dir);
23396                 } else {
23397                     newDate = new Date(this.date);
23398                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23399                     newViewDate = new Date(this.viewDate);
23400                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23401                 }
23402                 if (this.dateWithinRange(newDate)){
23403                     this.date = newDate;
23404                     this.viewDate = newViewDate;
23405                     this.setValue(this.formatDate(this.date));
23406 //                    this.update();
23407                     e.preventDefault();
23408                     dateChanged = true;
23409                 }
23410                 break;
23411             case 13: // enter
23412                 this.setValue(this.formatDate(this.date));
23413                 this.hidePopup();
23414                 e.preventDefault();
23415                 break;
23416             case 9: // tab
23417                 this.setValue(this.formatDate(this.date));
23418                 this.hidePopup();
23419                 break;
23420             case 16: // shift
23421             case 17: // ctrl
23422             case 18: // alt
23423                 break;
23424             default :
23425                 this.hidePopup();
23426                 
23427         }
23428     },
23429     
23430     
23431     onClick: function(e) 
23432     {
23433         e.stopPropagation();
23434         e.preventDefault();
23435         
23436         var target = e.getTarget();
23437         
23438         if(target.nodeName.toLowerCase() === 'i'){
23439             target = Roo.get(target).dom.parentNode;
23440         }
23441         
23442         var nodeName = target.nodeName;
23443         var className = target.className;
23444         var html = target.innerHTML;
23445         //Roo.log(nodeName);
23446         
23447         switch(nodeName.toLowerCase()) {
23448             case 'th':
23449                 switch(className) {
23450                     case 'switch':
23451                         this.showMode(1);
23452                         break;
23453                     case 'prev':
23454                     case 'next':
23455                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23456                         switch(this.viewMode){
23457                                 case 0:
23458                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23459                                         break;
23460                                 case 1:
23461                                 case 2:
23462                                         this.viewDate = this.moveYear(this.viewDate, dir);
23463                                         break;
23464                         }
23465                         this.fill();
23466                         break;
23467                     case 'today':
23468                         var date = new Date();
23469                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23470 //                        this.fill()
23471                         this.setValue(this.formatDate(this.date));
23472                         
23473                         this.hidePopup();
23474                         break;
23475                 }
23476                 break;
23477             case 'span':
23478                 if (className.indexOf('disabled') < 0) {
23479                 if (!this.viewDate) {
23480                     this.viewDate = new Date();
23481                 }
23482                 this.viewDate.setUTCDate(1);
23483                     if (className.indexOf('month') > -1) {
23484                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23485                     } else {
23486                         var year = parseInt(html, 10) || 0;
23487                         this.viewDate.setUTCFullYear(year);
23488                         
23489                     }
23490                     
23491                     if(this.singleMode){
23492                         this.setValue(this.formatDate(this.viewDate));
23493                         this.hidePopup();
23494                         return;
23495                     }
23496                     
23497                     this.showMode(-1);
23498                     this.fill();
23499                 }
23500                 break;
23501                 
23502             case 'td':
23503                 //Roo.log(className);
23504                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23505                     var day = parseInt(html, 10) || 1;
23506                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23507                         month = (this.viewDate || new Date()).getUTCMonth();
23508
23509                     if (className.indexOf('old') > -1) {
23510                         if(month === 0 ){
23511                             month = 11;
23512                             year -= 1;
23513                         }else{
23514                             month -= 1;
23515                         }
23516                     } else if (className.indexOf('new') > -1) {
23517                         if (month == 11) {
23518                             month = 0;
23519                             year += 1;
23520                         } else {
23521                             month += 1;
23522                         }
23523                     }
23524                     //Roo.log([year,month,day]);
23525                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23526                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23527 //                    this.fill();
23528                     //Roo.log(this.formatDate(this.date));
23529                     this.setValue(this.formatDate(this.date));
23530                     this.hidePopup();
23531                 }
23532                 break;
23533         }
23534     },
23535     
23536     setStartDate: function(startDate)
23537     {
23538         this.startDate = startDate || -Infinity;
23539         if (this.startDate !== -Infinity) {
23540             this.startDate = this.parseDate(this.startDate);
23541         }
23542         this.update();
23543         this.updateNavArrows();
23544     },
23545
23546     setEndDate: function(endDate)
23547     {
23548         this.endDate = endDate || Infinity;
23549         if (this.endDate !== Infinity) {
23550             this.endDate = this.parseDate(this.endDate);
23551         }
23552         this.update();
23553         this.updateNavArrows();
23554     },
23555     
23556     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23557     {
23558         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23559         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23560             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23561         }
23562         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23563             return parseInt(d, 10);
23564         });
23565         this.update();
23566         this.updateNavArrows();
23567     },
23568     
23569     updateNavArrows: function() 
23570     {
23571         if(this.singleMode){
23572             return;
23573         }
23574         
23575         var d = new Date(this.viewDate),
23576         year = d.getUTCFullYear(),
23577         month = d.getUTCMonth();
23578         
23579         Roo.each(this.picker().select('.prev', true).elements, function(v){
23580             v.show();
23581             switch (this.viewMode) {
23582                 case 0:
23583
23584                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23585                         v.hide();
23586                     }
23587                     break;
23588                 case 1:
23589                 case 2:
23590                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23591                         v.hide();
23592                     }
23593                     break;
23594             }
23595         });
23596         
23597         Roo.each(this.picker().select('.next', true).elements, function(v){
23598             v.show();
23599             switch (this.viewMode) {
23600                 case 0:
23601
23602                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23603                         v.hide();
23604                     }
23605                     break;
23606                 case 1:
23607                 case 2:
23608                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23609                         v.hide();
23610                     }
23611                     break;
23612             }
23613         })
23614     },
23615     
23616     moveMonth: function(date, dir)
23617     {
23618         if (!dir) {
23619             return date;
23620         }
23621         var new_date = new Date(date.valueOf()),
23622         day = new_date.getUTCDate(),
23623         month = new_date.getUTCMonth(),
23624         mag = Math.abs(dir),
23625         new_month, test;
23626         dir = dir > 0 ? 1 : -1;
23627         if (mag == 1){
23628             test = dir == -1
23629             // If going back one month, make sure month is not current month
23630             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23631             ? function(){
23632                 return new_date.getUTCMonth() == month;
23633             }
23634             // If going forward one month, make sure month is as expected
23635             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23636             : function(){
23637                 return new_date.getUTCMonth() != new_month;
23638             };
23639             new_month = month + dir;
23640             new_date.setUTCMonth(new_month);
23641             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23642             if (new_month < 0 || new_month > 11) {
23643                 new_month = (new_month + 12) % 12;
23644             }
23645         } else {
23646             // For magnitudes >1, move one month at a time...
23647             for (var i=0; i<mag; i++) {
23648                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23649                 new_date = this.moveMonth(new_date, dir);
23650             }
23651             // ...then reset the day, keeping it in the new month
23652             new_month = new_date.getUTCMonth();
23653             new_date.setUTCDate(day);
23654             test = function(){
23655                 return new_month != new_date.getUTCMonth();
23656             };
23657         }
23658         // Common date-resetting loop -- if date is beyond end of month, make it
23659         // end of month
23660         while (test()){
23661             new_date.setUTCDate(--day);
23662             new_date.setUTCMonth(new_month);
23663         }
23664         return new_date;
23665     },
23666
23667     moveYear: function(date, dir)
23668     {
23669         return this.moveMonth(date, dir*12);
23670     },
23671
23672     dateWithinRange: function(date)
23673     {
23674         return date >= this.startDate && date <= this.endDate;
23675     },
23676
23677     
23678     remove: function() 
23679     {
23680         this.picker().remove();
23681     },
23682     
23683     validateValue : function(value)
23684     {
23685         if(this.getVisibilityEl().hasClass('hidden')){
23686             return true;
23687         }
23688         
23689         if(value.length < 1)  {
23690             if(this.allowBlank){
23691                 return true;
23692             }
23693             return false;
23694         }
23695         
23696         if(value.length < this.minLength){
23697             return false;
23698         }
23699         if(value.length > this.maxLength){
23700             return false;
23701         }
23702         if(this.vtype){
23703             var vt = Roo.form.VTypes;
23704             if(!vt[this.vtype](value, this)){
23705                 return false;
23706             }
23707         }
23708         if(typeof this.validator == "function"){
23709             var msg = this.validator(value);
23710             if(msg !== true){
23711                 return false;
23712             }
23713         }
23714         
23715         if(this.regex && !this.regex.test(value)){
23716             return false;
23717         }
23718         
23719         if(typeof(this.parseDate(value)) == 'undefined'){
23720             return false;
23721         }
23722         
23723         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23724             return false;
23725         }      
23726         
23727         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23728             return false;
23729         } 
23730         
23731         
23732         return true;
23733     },
23734     
23735     reset : function()
23736     {
23737         this.date = this.viewDate = '';
23738         
23739         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23740     }
23741    
23742 });
23743
23744 Roo.apply(Roo.bootstrap.form.DateField,  {
23745     
23746     head : {
23747         tag: 'thead',
23748         cn: [
23749         {
23750             tag: 'tr',
23751             cn: [
23752             {
23753                 tag: 'th',
23754                 cls: 'prev',
23755                 html: '<i class="fa fa-arrow-left"/>'
23756             },
23757             {
23758                 tag: 'th',
23759                 cls: 'switch',
23760                 colspan: '5'
23761             },
23762             {
23763                 tag: 'th',
23764                 cls: 'next',
23765                 html: '<i class="fa fa-arrow-right"/>'
23766             }
23767
23768             ]
23769         }
23770         ]
23771     },
23772     
23773     content : {
23774         tag: 'tbody',
23775         cn: [
23776         {
23777             tag: 'tr',
23778             cn: [
23779             {
23780                 tag: 'td',
23781                 colspan: '7'
23782             }
23783             ]
23784         }
23785         ]
23786     },
23787     
23788     footer : {
23789         tag: 'tfoot',
23790         cn: [
23791         {
23792             tag: 'tr',
23793             cn: [
23794             {
23795                 tag: 'th',
23796                 colspan: '7',
23797                 cls: 'today'
23798             }
23799                     
23800             ]
23801         }
23802         ]
23803     },
23804     
23805     dates:{
23806         en: {
23807             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23808             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23809             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23810             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23811             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23812             today: "Today"
23813         }
23814     },
23815     
23816     modes: [
23817     {
23818         clsName: 'days',
23819         navFnc: 'Month',
23820         navStep: 1
23821     },
23822     {
23823         clsName: 'months',
23824         navFnc: 'FullYear',
23825         navStep: 1
23826     },
23827     {
23828         clsName: 'years',
23829         navFnc: 'FullYear',
23830         navStep: 10
23831     }]
23832 });
23833
23834 Roo.apply(Roo.bootstrap.form.DateField,  {
23835   
23836     template : {
23837         tag: 'div',
23838         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23839         cn: [
23840         {
23841             tag: 'div',
23842             cls: 'datepicker-days',
23843             cn: [
23844             {
23845                 tag: 'table',
23846                 cls: 'table-condensed',
23847                 cn:[
23848                 Roo.bootstrap.form.DateField.head,
23849                 {
23850                     tag: 'tbody'
23851                 },
23852                 Roo.bootstrap.form.DateField.footer
23853                 ]
23854             }
23855             ]
23856         },
23857         {
23858             tag: 'div',
23859             cls: 'datepicker-months',
23860             cn: [
23861             {
23862                 tag: 'table',
23863                 cls: 'table-condensed',
23864                 cn:[
23865                 Roo.bootstrap.form.DateField.head,
23866                 Roo.bootstrap.form.DateField.content,
23867                 Roo.bootstrap.form.DateField.footer
23868                 ]
23869             }
23870             ]
23871         },
23872         {
23873             tag: 'div',
23874             cls: 'datepicker-years',
23875             cn: [
23876             {
23877                 tag: 'table',
23878                 cls: 'table-condensed',
23879                 cn:[
23880                 Roo.bootstrap.form.DateField.head,
23881                 Roo.bootstrap.form.DateField.content,
23882                 Roo.bootstrap.form.DateField.footer
23883                 ]
23884             }
23885             ]
23886         }
23887         ]
23888     }
23889 });
23890
23891  
23892
23893  /*
23894  * - LGPL
23895  *
23896  * TimeField
23897  * 
23898  */
23899
23900 /**
23901  * @class Roo.bootstrap.form.TimeField
23902  * @extends Roo.bootstrap.form.Input
23903  * Bootstrap DateField class
23904  * 
23905  * 
23906  * @constructor
23907  * Create a new TimeField
23908  * @param {Object} config The config object
23909  */
23910
23911 Roo.bootstrap.form.TimeField = function(config){
23912     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23913     this.addEvents({
23914             /**
23915              * @event show
23916              * Fires when this field show.
23917              * @param {Roo.bootstrap.form.DateField} thisthis
23918              * @param {Mixed} date The date value
23919              */
23920             show : true,
23921             /**
23922              * @event show
23923              * Fires when this field hide.
23924              * @param {Roo.bootstrap.form.DateField} this
23925              * @param {Mixed} date The date value
23926              */
23927             hide : true,
23928             /**
23929              * @event select
23930              * Fires when select a date.
23931              * @param {Roo.bootstrap.form.DateField} this
23932              * @param {Mixed} date The date value
23933              */
23934             select : true
23935         });
23936 };
23937
23938 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23939     
23940     /**
23941      * @cfg {String} format
23942      * The default time format string which can be overriden for localization support.  The format must be
23943      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23944      */
23945     format : "H:i",
23946
23947     getAutoCreate : function()
23948     {
23949         this.after = '<i class="fa far fa-clock"></i>';
23950         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23951         
23952          
23953     },
23954     onRender: function(ct, position)
23955     {
23956         
23957         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23958                 
23959         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23960         
23961         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23962         
23963         this.pop = this.picker().select('>.datepicker-time',true).first();
23964         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23965         
23966         this.picker().on('mousedown', this.onMousedown, this);
23967         this.picker().on('click', this.onClick, this);
23968         
23969         this.picker().addClass('datepicker-dropdown');
23970     
23971         this.fillTime();
23972         this.update();
23973             
23974         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23975         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23976         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23977         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23978         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23979         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23980
23981     },
23982     
23983     fireKey: function(e){
23984         if (!this.picker().isVisible()){
23985             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23986                 this.show();
23987             }
23988             return;
23989         }
23990
23991         e.preventDefault();
23992         
23993         switch(e.keyCode){
23994             case 27: // escape
23995                 this.hide();
23996                 break;
23997             case 37: // left
23998             case 39: // right
23999                 this.onTogglePeriod();
24000                 break;
24001             case 38: // up
24002                 this.onIncrementMinutes();
24003                 break;
24004             case 40: // down
24005                 this.onDecrementMinutes();
24006                 break;
24007             case 13: // enter
24008             case 9: // tab
24009                 this.setTime();
24010                 break;
24011         }
24012     },
24013     
24014     onClick: function(e) {
24015         e.stopPropagation();
24016         e.preventDefault();
24017     },
24018     
24019     picker : function()
24020     {
24021         return this.pickerEl;
24022     },
24023     
24024     fillTime: function()
24025     {    
24026         var time = this.pop.select('tbody', true).first();
24027         
24028         time.dom.innerHTML = '';
24029         
24030         time.createChild({
24031             tag: 'tr',
24032             cn: [
24033                 {
24034                     tag: 'td',
24035                     cn: [
24036                         {
24037                             tag: 'a',
24038                             href: '#',
24039                             cls: 'btn',
24040                             cn: [
24041                                 {
24042                                     tag: 'i',
24043                                     cls: 'hours-up fa fas fa-chevron-up'
24044                                 }
24045                             ]
24046                         } 
24047                     ]
24048                 },
24049                 {
24050                     tag: 'td',
24051                     cls: 'separator'
24052                 },
24053                 {
24054                     tag: 'td',
24055                     cn: [
24056                         {
24057                             tag: 'a',
24058                             href: '#',
24059                             cls: 'btn',
24060                             cn: [
24061                                 {
24062                                     tag: 'i',
24063                                     cls: 'minutes-up fa fas fa-chevron-up'
24064                                 }
24065                             ]
24066                         }
24067                     ]
24068                 },
24069                 {
24070                     tag: 'td',
24071                     cls: 'separator'
24072                 }
24073             ]
24074         });
24075         
24076         time.createChild({
24077             tag: 'tr',
24078             cn: [
24079                 {
24080                     tag: 'td',
24081                     cn: [
24082                         {
24083                             tag: 'span',
24084                             cls: 'timepicker-hour',
24085                             html: '00'
24086                         }  
24087                     ]
24088                 },
24089                 {
24090                     tag: 'td',
24091                     cls: 'separator',
24092                     html: ':'
24093                 },
24094                 {
24095                     tag: 'td',
24096                     cn: [
24097                         {
24098                             tag: 'span',
24099                             cls: 'timepicker-minute',
24100                             html: '00'
24101                         }  
24102                     ]
24103                 },
24104                 {
24105                     tag: 'td',
24106                     cls: 'separator'
24107                 },
24108                 {
24109                     tag: 'td',
24110                     cn: [
24111                         {
24112                             tag: 'button',
24113                             type: 'button',
24114                             cls: 'btn btn-primary period',
24115                             html: 'AM'
24116                             
24117                         }
24118                     ]
24119                 }
24120             ]
24121         });
24122         
24123         time.createChild({
24124             tag: 'tr',
24125             cn: [
24126                 {
24127                     tag: 'td',
24128                     cn: [
24129                         {
24130                             tag: 'a',
24131                             href: '#',
24132                             cls: 'btn',
24133                             cn: [
24134                                 {
24135                                     tag: 'span',
24136                                     cls: 'hours-down fa fas fa-chevron-down'
24137                                 }
24138                             ]
24139                         }
24140                     ]
24141                 },
24142                 {
24143                     tag: 'td',
24144                     cls: 'separator'
24145                 },
24146                 {
24147                     tag: 'td',
24148                     cn: [
24149                         {
24150                             tag: 'a',
24151                             href: '#',
24152                             cls: 'btn',
24153                             cn: [
24154                                 {
24155                                     tag: 'span',
24156                                     cls: 'minutes-down fa fas fa-chevron-down'
24157                                 }
24158                             ]
24159                         }
24160                     ]
24161                 },
24162                 {
24163                     tag: 'td',
24164                     cls: 'separator'
24165                 }
24166             ]
24167         });
24168         
24169     },
24170     
24171     update: function()
24172     {
24173         
24174         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24175         
24176         this.fill();
24177     },
24178     
24179     fill: function() 
24180     {
24181         var hours = this.time.getHours();
24182         var minutes = this.time.getMinutes();
24183         var period = 'AM';
24184         
24185         if(hours > 11){
24186             period = 'PM';
24187         }
24188         
24189         if(hours == 0){
24190             hours = 12;
24191         }
24192         
24193         
24194         if(hours > 12){
24195             hours = hours - 12;
24196         }
24197         
24198         if(hours < 10){
24199             hours = '0' + hours;
24200         }
24201         
24202         if(minutes < 10){
24203             minutes = '0' + minutes;
24204         }
24205         
24206         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24207         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24208         this.pop.select('button', true).first().dom.innerHTML = period;
24209         
24210     },
24211     
24212     place: function()
24213     {   
24214         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24215         
24216         var cls = ['bottom'];
24217         
24218         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24219             cls.pop();
24220             cls.push('top');
24221         }
24222         
24223         cls.push('right');
24224         
24225         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24226             cls.pop();
24227             cls.push('left');
24228         }
24229         //this.picker().setXY(20000,20000);
24230         this.picker().addClass(cls.join('-'));
24231         
24232         var _this = this;
24233         
24234         Roo.each(cls, function(c){
24235             if(c == 'bottom'){
24236                 (function() {
24237                  //  
24238                 }).defer(200);
24239                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24240                 //_this.picker().setTop(_this.inputEl().getHeight());
24241                 return;
24242             }
24243             if(c == 'top'){
24244                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24245                 
24246                 //_this.picker().setTop(0 - _this.picker().getHeight());
24247                 return;
24248             }
24249             /*
24250             if(c == 'left'){
24251                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24252                 return;
24253             }
24254             if(c == 'right'){
24255                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24256                 return;
24257             }
24258             */
24259         });
24260         
24261     },
24262   
24263     onFocus : function()
24264     {
24265         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24266         this.show();
24267     },
24268     
24269     onBlur : function()
24270     {
24271         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24272         this.hide();
24273     },
24274     
24275     show : function()
24276     {
24277         this.picker().show();
24278         this.pop.show();
24279         this.update();
24280         this.place();
24281         
24282         this.fireEvent('show', this, this.date);
24283     },
24284     
24285     hide : function()
24286     {
24287         this.picker().hide();
24288         this.pop.hide();
24289         
24290         this.fireEvent('hide', this, this.date);
24291     },
24292     
24293     setTime : function()
24294     {
24295         this.hide();
24296         this.setValue(this.time.format(this.format));
24297         
24298         this.fireEvent('select', this, this.date);
24299         
24300         
24301     },
24302     
24303     onMousedown: function(e){
24304         e.stopPropagation();
24305         e.preventDefault();
24306     },
24307     
24308     onIncrementHours: function()
24309     {
24310         Roo.log('onIncrementHours');
24311         this.time = this.time.add(Date.HOUR, 1);
24312         this.update();
24313         
24314     },
24315     
24316     onDecrementHours: function()
24317     {
24318         Roo.log('onDecrementHours');
24319         this.time = this.time.add(Date.HOUR, -1);
24320         this.update();
24321     },
24322     
24323     onIncrementMinutes: function()
24324     {
24325         Roo.log('onIncrementMinutes');
24326         this.time = this.time.add(Date.MINUTE, 1);
24327         this.update();
24328     },
24329     
24330     onDecrementMinutes: function()
24331     {
24332         Roo.log('onDecrementMinutes');
24333         this.time = this.time.add(Date.MINUTE, -1);
24334         this.update();
24335     },
24336     
24337     onTogglePeriod: function()
24338     {
24339         Roo.log('onTogglePeriod');
24340         this.time = this.time.add(Date.HOUR, 12);
24341         this.update();
24342     }
24343     
24344    
24345 });
24346  
24347
24348 Roo.apply(Roo.bootstrap.form.TimeField,  {
24349   
24350     template : {
24351         tag: 'div',
24352         cls: 'datepicker dropdown-menu',
24353         cn: [
24354             {
24355                 tag: 'div',
24356                 cls: 'datepicker-time',
24357                 cn: [
24358                 {
24359                     tag: 'table',
24360                     cls: 'table-condensed',
24361                     cn:[
24362                         {
24363                             tag: 'tbody',
24364                             cn: [
24365                                 {
24366                                     tag: 'tr',
24367                                     cn: [
24368                                     {
24369                                         tag: 'td',
24370                                         colspan: '7'
24371                                     }
24372                                     ]
24373                                 }
24374                             ]
24375                         },
24376                         {
24377                             tag: 'tfoot',
24378                             cn: [
24379                                 {
24380                                     tag: 'tr',
24381                                     cn: [
24382                                     {
24383                                         tag: 'th',
24384                                         colspan: '7',
24385                                         cls: '',
24386                                         cn: [
24387                                             {
24388                                                 tag: 'button',
24389                                                 cls: 'btn btn-info ok',
24390                                                 html: 'OK'
24391                                             }
24392                                         ]
24393                                     }
24394                     
24395                                     ]
24396                                 }
24397                             ]
24398                         }
24399                     ]
24400                 }
24401                 ]
24402             }
24403         ]
24404     }
24405 });
24406
24407  
24408
24409  /*
24410  * - LGPL
24411  *
24412  * MonthField
24413  * 
24414  */
24415
24416 /**
24417  * @class Roo.bootstrap.form.MonthField
24418  * @extends Roo.bootstrap.form.Input
24419  * Bootstrap MonthField class
24420  * 
24421  * @cfg {String} language default en
24422  * 
24423  * @constructor
24424  * Create a new MonthField
24425  * @param {Object} config The config object
24426  */
24427
24428 Roo.bootstrap.form.MonthField = function(config){
24429     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24430     
24431     this.addEvents({
24432         /**
24433          * @event show
24434          * Fires when this field show.
24435          * @param {Roo.bootstrap.form.MonthField} this
24436          * @param {Mixed} date The date value
24437          */
24438         show : true,
24439         /**
24440          * @event show
24441          * Fires when this field hide.
24442          * @param {Roo.bootstrap.form.MonthField} this
24443          * @param {Mixed} date The date value
24444          */
24445         hide : true,
24446         /**
24447          * @event select
24448          * Fires when select a date.
24449          * @param {Roo.bootstrap.form.MonthField} this
24450          * @param {String} oldvalue The old value
24451          * @param {String} newvalue The new value
24452          */
24453         select : true
24454     });
24455 };
24456
24457 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24458     
24459     onRender: function(ct, position)
24460     {
24461         
24462         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24463         
24464         this.language = this.language || 'en';
24465         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24466         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24467         
24468         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24469         this.isInline = false;
24470         this.isInput = true;
24471         this.component = this.el.select('.add-on', true).first() || false;
24472         this.component = (this.component && this.component.length === 0) ? false : this.component;
24473         this.hasInput = this.component && this.inputEL().length;
24474         
24475         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24476         
24477         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24478         
24479         this.picker().on('mousedown', this.onMousedown, this);
24480         this.picker().on('click', this.onClick, this);
24481         
24482         this.picker().addClass('datepicker-dropdown');
24483         
24484         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24485             v.setStyle('width', '189px');
24486         });
24487         
24488         this.fillMonths();
24489         
24490         this.update();
24491         
24492         if(this.isInline) {
24493             this.show();
24494         }
24495         
24496     },
24497     
24498     setValue: function(v, suppressEvent)
24499     {   
24500         var o = this.getValue();
24501         
24502         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24503         
24504         this.update();
24505
24506         if(suppressEvent !== true){
24507             this.fireEvent('select', this, o, v);
24508         }
24509         
24510     },
24511     
24512     getValue: function()
24513     {
24514         return this.value;
24515     },
24516     
24517     onClick: function(e) 
24518     {
24519         e.stopPropagation();
24520         e.preventDefault();
24521         
24522         var target = e.getTarget();
24523         
24524         if(target.nodeName.toLowerCase() === 'i'){
24525             target = Roo.get(target).dom.parentNode;
24526         }
24527         
24528         var nodeName = target.nodeName;
24529         var className = target.className;
24530         var html = target.innerHTML;
24531         
24532         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24533             return;
24534         }
24535         
24536         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24537         
24538         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24539         
24540         this.hide();
24541                         
24542     },
24543     
24544     picker : function()
24545     {
24546         return this.pickerEl;
24547     },
24548     
24549     fillMonths: function()
24550     {    
24551         var i = 0;
24552         var months = this.picker().select('>.datepicker-months td', true).first();
24553         
24554         months.dom.innerHTML = '';
24555         
24556         while (i < 12) {
24557             var month = {
24558                 tag: 'span',
24559                 cls: 'month',
24560                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24561             };
24562             
24563             months.createChild(month);
24564         }
24565         
24566     },
24567     
24568     update: function()
24569     {
24570         var _this = this;
24571         
24572         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24573             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24574         }
24575         
24576         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24577             e.removeClass('active');
24578             
24579             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24580                 e.addClass('active');
24581             }
24582         })
24583     },
24584     
24585     place: function()
24586     {
24587         if(this.isInline) {
24588             return;
24589         }
24590         
24591         this.picker().removeClass(['bottom', 'top']);
24592         
24593         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24594             /*
24595              * place to the top of element!
24596              *
24597              */
24598             
24599             this.picker().addClass('top');
24600             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24601             
24602             return;
24603         }
24604         
24605         this.picker().addClass('bottom');
24606         
24607         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24608     },
24609     
24610     onFocus : function()
24611     {
24612         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24613         this.show();
24614     },
24615     
24616     onBlur : function()
24617     {
24618         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24619         
24620         var d = this.inputEl().getValue();
24621         
24622         this.setValue(d);
24623                 
24624         this.hide();
24625     },
24626     
24627     show : function()
24628     {
24629         this.picker().show();
24630         this.picker().select('>.datepicker-months', true).first().show();
24631         this.update();
24632         this.place();
24633         
24634         this.fireEvent('show', this, this.date);
24635     },
24636     
24637     hide : function()
24638     {
24639         if(this.isInline) {
24640             return;
24641         }
24642         this.picker().hide();
24643         this.fireEvent('hide', this, this.date);
24644         
24645     },
24646     
24647     onMousedown: function(e)
24648     {
24649         e.stopPropagation();
24650         e.preventDefault();
24651     },
24652     
24653     keyup: function(e)
24654     {
24655         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24656         this.update();
24657     },
24658
24659     fireKey: function(e)
24660     {
24661         if (!this.picker().isVisible()){
24662             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24663                 this.show();
24664             }
24665             return;
24666         }
24667         
24668         var dir;
24669         
24670         switch(e.keyCode){
24671             case 27: // escape
24672                 this.hide();
24673                 e.preventDefault();
24674                 break;
24675             case 37: // left
24676             case 39: // right
24677                 dir = e.keyCode == 37 ? -1 : 1;
24678                 
24679                 this.vIndex = this.vIndex + dir;
24680                 
24681                 if(this.vIndex < 0){
24682                     this.vIndex = 0;
24683                 }
24684                 
24685                 if(this.vIndex > 11){
24686                     this.vIndex = 11;
24687                 }
24688                 
24689                 if(isNaN(this.vIndex)){
24690                     this.vIndex = 0;
24691                 }
24692                 
24693                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24694                 
24695                 break;
24696             case 38: // up
24697             case 40: // down
24698                 
24699                 dir = e.keyCode == 38 ? -1 : 1;
24700                 
24701                 this.vIndex = this.vIndex + dir * 4;
24702                 
24703                 if(this.vIndex < 0){
24704                     this.vIndex = 0;
24705                 }
24706                 
24707                 if(this.vIndex > 11){
24708                     this.vIndex = 11;
24709                 }
24710                 
24711                 if(isNaN(this.vIndex)){
24712                     this.vIndex = 0;
24713                 }
24714                 
24715                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24716                 break;
24717                 
24718             case 13: // enter
24719                 
24720                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24721                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24722                 }
24723                 
24724                 this.hide();
24725                 e.preventDefault();
24726                 break;
24727             case 9: // tab
24728                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24729                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24730                 }
24731                 this.hide();
24732                 break;
24733             case 16: // shift
24734             case 17: // ctrl
24735             case 18: // alt
24736                 break;
24737             default :
24738                 this.hide();
24739                 
24740         }
24741     },
24742     
24743     remove: function() 
24744     {
24745         this.picker().remove();
24746     }
24747    
24748 });
24749
24750 Roo.apply(Roo.bootstrap.form.MonthField,  {
24751     
24752     content : {
24753         tag: 'tbody',
24754         cn: [
24755         {
24756             tag: 'tr',
24757             cn: [
24758             {
24759                 tag: 'td',
24760                 colspan: '7'
24761             }
24762             ]
24763         }
24764         ]
24765     },
24766     
24767     dates:{
24768         en: {
24769             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24770             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24771         }
24772     }
24773 });
24774
24775 Roo.apply(Roo.bootstrap.form.MonthField,  {
24776   
24777     template : {
24778         tag: 'div',
24779         cls: 'datepicker dropdown-menu roo-dynamic',
24780         cn: [
24781             {
24782                 tag: 'div',
24783                 cls: 'datepicker-months',
24784                 cn: [
24785                 {
24786                     tag: 'table',
24787                     cls: 'table-condensed',
24788                     cn:[
24789                         Roo.bootstrap.form.DateField.content
24790                     ]
24791                 }
24792                 ]
24793             }
24794         ]
24795     }
24796 });
24797
24798  
24799
24800  
24801  /*
24802  * - LGPL
24803  *
24804  * CheckBox
24805  * 
24806  */
24807
24808 /**
24809  * @class Roo.bootstrap.form.CheckBox
24810  * @extends Roo.bootstrap.form.Input
24811  * Bootstrap CheckBox class
24812  * 
24813  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24814  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24815  * @cfg {String} boxLabel The text that appears beside the checkbox
24816  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24817  * @cfg {Boolean} checked initnal the element
24818  * @cfg {Boolean} inline inline the element (default false)
24819  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24820  * @cfg {String} tooltip label tooltip
24821  * 
24822  * @constructor
24823  * Create a new CheckBox
24824  * @param {Object} config The config object
24825  */
24826
24827 Roo.bootstrap.form.CheckBox = function(config){
24828     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24829    
24830     this.addEvents({
24831         /**
24832         * @event check
24833         * Fires when the element is checked or unchecked.
24834         * @param {Roo.bootstrap.form.CheckBox} this This input
24835         * @param {Boolean} checked The new checked value
24836         */
24837        check : true,
24838        /**
24839         * @event click
24840         * Fires when the element is click.
24841         * @param {Roo.bootstrap.form.CheckBox} this This input
24842         */
24843        click : true
24844     });
24845     
24846 };
24847
24848 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24849   
24850     inputType: 'checkbox',
24851     inputValue: 1,
24852     valueOff: 0,
24853     boxLabel: false,
24854     checked: false,
24855     weight : false,
24856     inline: false,
24857     tooltip : '',
24858     
24859     // checkbox success does not make any sense really.. 
24860     invalidClass : "",
24861     validClass : "",
24862     
24863     
24864     getAutoCreate : function()
24865     {
24866         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24867         
24868         var id = Roo.id();
24869         
24870         var cfg = {};
24871         
24872         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24873         
24874         if(this.inline){
24875             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24876         }
24877         
24878         var input =  {
24879             tag: 'input',
24880             id : id,
24881             type : this.inputType,
24882             value : this.inputValue,
24883             cls : 'roo-' + this.inputType, //'form-box',
24884             placeholder : this.placeholder || ''
24885             
24886         };
24887         
24888         if(this.inputType != 'radio'){
24889             var hidden =  {
24890                 tag: 'input',
24891                 type : 'hidden',
24892                 cls : 'roo-hidden-value',
24893                 value : this.checked ? this.inputValue : this.valueOff
24894             };
24895         }
24896         
24897             
24898         if (this.weight) { // Validity check?
24899             cfg.cls += " " + this.inputType + "-" + this.weight;
24900         }
24901         
24902         if (this.disabled) {
24903             input.disabled=true;
24904         }
24905         
24906         if(this.checked){
24907             input.checked = this.checked;
24908         }
24909         
24910         if (this.name) {
24911             
24912             input.name = this.name;
24913             
24914             if(this.inputType != 'radio'){
24915                 hidden.name = this.name;
24916                 input.name = '_hidden_' + this.name;
24917             }
24918         }
24919         
24920         if (this.size) {
24921             input.cls += ' input-' + this.size;
24922         }
24923         
24924         var settings=this;
24925         
24926         ['xs','sm','md','lg'].map(function(size){
24927             if (settings[size]) {
24928                 cfg.cls += ' col-' + size + '-' + settings[size];
24929             }
24930         });
24931         
24932         var inputblock = input;
24933          
24934         if (this.before || this.after) {
24935             
24936             inputblock = {
24937                 cls : 'input-group',
24938                 cn :  [] 
24939             };
24940             
24941             if (this.before) {
24942                 inputblock.cn.push({
24943                     tag :'span',
24944                     cls : 'input-group-addon',
24945                     html : this.before
24946                 });
24947             }
24948             
24949             inputblock.cn.push(input);
24950             
24951             if(this.inputType != 'radio'){
24952                 inputblock.cn.push(hidden);
24953             }
24954             
24955             if (this.after) {
24956                 inputblock.cn.push({
24957                     tag :'span',
24958                     cls : 'input-group-addon',
24959                     html : this.after
24960                 });
24961             }
24962             
24963         }
24964         var boxLabelCfg = false;
24965         
24966         if(this.boxLabel){
24967            
24968             boxLabelCfg = {
24969                 tag: 'label',
24970                 //'for': id, // box label is handled by onclick - so no for...
24971                 cls: 'box-label',
24972                 html: this.boxLabel
24973             };
24974             if(this.tooltip){
24975                 boxLabelCfg.tooltip = this.tooltip;
24976             }
24977              
24978         }
24979         
24980         
24981         if (align ==='left' && this.fieldLabel.length) {
24982 //                Roo.log("left and has label");
24983             cfg.cn = [
24984                 {
24985                     tag: 'label',
24986                     'for' :  id,
24987                     cls : 'control-label',
24988                     html : this.fieldLabel
24989                 },
24990                 {
24991                     cls : "", 
24992                     cn: [
24993                         inputblock
24994                     ]
24995                 }
24996             ];
24997             
24998             if (boxLabelCfg) {
24999                 cfg.cn[1].cn.push(boxLabelCfg);
25000             }
25001             
25002             if(this.labelWidth > 12){
25003                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25004             }
25005             
25006             if(this.labelWidth < 13 && this.labelmd == 0){
25007                 this.labelmd = this.labelWidth;
25008             }
25009             
25010             if(this.labellg > 0){
25011                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25012                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25013             }
25014             
25015             if(this.labelmd > 0){
25016                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25017                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25018             }
25019             
25020             if(this.labelsm > 0){
25021                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25022                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25023             }
25024             
25025             if(this.labelxs > 0){
25026                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25027                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25028             }
25029             
25030         } else if ( this.fieldLabel.length) {
25031 //                Roo.log(" label");
25032                 cfg.cn = [
25033                    
25034                     {
25035                         tag: this.boxLabel ? 'span' : 'label',
25036                         'for': id,
25037                         cls: 'control-label box-input-label',
25038                         //cls : 'input-group-addon',
25039                         html : this.fieldLabel
25040                     },
25041                     
25042                     inputblock
25043                     
25044                 ];
25045                 if (boxLabelCfg) {
25046                     cfg.cn.push(boxLabelCfg);
25047                 }
25048
25049         } else {
25050             
25051 //                Roo.log(" no label && no align");
25052                 cfg.cn = [  inputblock ] ;
25053                 if (boxLabelCfg) {
25054                     cfg.cn.push(boxLabelCfg);
25055                 }
25056
25057                 
25058         }
25059         
25060        
25061         
25062         if(this.inputType != 'radio'){
25063             cfg.cn.push(hidden);
25064         }
25065         
25066         return cfg;
25067         
25068     },
25069     
25070     /**
25071      * return the real input element.
25072      */
25073     inputEl: function ()
25074     {
25075         return this.el.select('input.roo-' + this.inputType,true).first();
25076     },
25077     hiddenEl: function ()
25078     {
25079         return this.el.select('input.roo-hidden-value',true).first();
25080     },
25081     
25082     labelEl: function()
25083     {
25084         return this.el.select('label.control-label',true).first();
25085     },
25086     /* depricated... */
25087     
25088     label: function()
25089     {
25090         return this.labelEl();
25091     },
25092     
25093     boxLabelEl: function()
25094     {
25095         return this.el.select('label.box-label',true).first();
25096     },
25097     
25098     initEvents : function()
25099     {
25100 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25101         
25102         this.inputEl().on('click', this.onClick,  this);
25103         
25104         if (this.boxLabel) { 
25105             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25106         }
25107         
25108         this.startValue = this.getValue();
25109         
25110         if(this.groupId){
25111             Roo.bootstrap.form.CheckBox.register(this);
25112         }
25113     },
25114     
25115     onClick : function(e)
25116     {   
25117         if(this.fireEvent('click', this, e) !== false){
25118             this.setChecked(!this.checked);
25119         }
25120         
25121     },
25122     
25123     setChecked : function(state,suppressEvent)
25124     {
25125         this.startValue = this.getValue();
25126
25127         if(this.inputType == 'radio'){
25128             
25129             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25130                 e.dom.checked = false;
25131             });
25132             
25133             this.inputEl().dom.checked = true;
25134             
25135             this.inputEl().dom.value = this.inputValue;
25136             
25137             if(suppressEvent !== true){
25138                 this.fireEvent('check', this, true);
25139             }
25140             
25141             this.validate();
25142             
25143             return;
25144         }
25145         
25146         this.checked = state;
25147         
25148         this.inputEl().dom.checked = state;
25149         
25150         
25151         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25152         
25153         if(suppressEvent !== true){
25154             this.fireEvent('check', this, state);
25155         }
25156         
25157         this.validate();
25158     },
25159     
25160     getValue : function()
25161     {
25162         if(this.inputType == 'radio'){
25163             return this.getGroupValue();
25164         }
25165         
25166         return this.hiddenEl().dom.value;
25167         
25168     },
25169     
25170     getGroupValue : function()
25171     {
25172         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25173             return '';
25174         }
25175         
25176         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25177     },
25178     
25179     setValue : function(v,suppressEvent)
25180     {
25181         if(this.inputType == 'radio'){
25182             this.setGroupValue(v, suppressEvent);
25183             return;
25184         }
25185         
25186         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25187         
25188         this.validate();
25189     },
25190     
25191     setGroupValue : function(v, suppressEvent)
25192     {
25193         this.startValue = this.getValue();
25194         
25195         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25196             e.dom.checked = false;
25197             
25198             if(e.dom.value == v){
25199                 e.dom.checked = true;
25200             }
25201         });
25202         
25203         if(suppressEvent !== true){
25204             this.fireEvent('check', this, true);
25205         }
25206
25207         this.validate();
25208         
25209         return;
25210     },
25211     
25212     validate : function()
25213     {
25214         if(this.getVisibilityEl().hasClass('hidden')){
25215             return true;
25216         }
25217         
25218         if(
25219                 this.disabled || 
25220                 (this.inputType == 'radio' && this.validateRadio()) ||
25221                 (this.inputType == 'checkbox' && this.validateCheckbox())
25222         ){
25223             this.markValid();
25224             return true;
25225         }
25226         
25227         this.markInvalid();
25228         return false;
25229     },
25230     
25231     validateRadio : function()
25232     {
25233         if(this.getVisibilityEl().hasClass('hidden')){
25234             return true;
25235         }
25236         
25237         if(this.allowBlank){
25238             return true;
25239         }
25240         
25241         var valid = false;
25242         
25243         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25244             if(!e.dom.checked){
25245                 return;
25246             }
25247             
25248             valid = true;
25249             
25250             return false;
25251         });
25252         
25253         return valid;
25254     },
25255     
25256     validateCheckbox : function()
25257     {
25258         if(!this.groupId){
25259             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25260             //return (this.getValue() == this.inputValue) ? true : false;
25261         }
25262         
25263         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25264         
25265         if(!group){
25266             return false;
25267         }
25268         
25269         var r = false;
25270         
25271         for(var i in group){
25272             if(group[i].el.isVisible(true)){
25273                 r = false;
25274                 break;
25275             }
25276             
25277             r = true;
25278         }
25279         
25280         for(var i in group){
25281             if(r){
25282                 break;
25283             }
25284             
25285             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25286         }
25287         
25288         return r;
25289     },
25290     
25291     /**
25292      * Mark this field as valid
25293      */
25294     markValid : function()
25295     {
25296         var _this = this;
25297         
25298         this.fireEvent('valid', this);
25299         
25300         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25301         
25302         if(this.groupId){
25303             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25304         }
25305         
25306         if(label){
25307             label.markValid();
25308         }
25309
25310         if(this.inputType == 'radio'){
25311             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25312                 var fg = e.findParent('.form-group', false, true);
25313                 if (Roo.bootstrap.version == 3) {
25314                     fg.removeClass([_this.invalidClass, _this.validClass]);
25315                     fg.addClass(_this.validClass);
25316                 } else {
25317                     fg.removeClass(['is-valid', 'is-invalid']);
25318                     fg.addClass('is-valid');
25319                 }
25320             });
25321             
25322             return;
25323         }
25324
25325         if(!this.groupId){
25326             var fg = this.el.findParent('.form-group', false, true);
25327             if (Roo.bootstrap.version == 3) {
25328                 fg.removeClass([this.invalidClass, this.validClass]);
25329                 fg.addClass(this.validClass);
25330             } else {
25331                 fg.removeClass(['is-valid', 'is-invalid']);
25332                 fg.addClass('is-valid');
25333             }
25334             return;
25335         }
25336         
25337         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25338         
25339         if(!group){
25340             return;
25341         }
25342         
25343         for(var i in group){
25344             var fg = group[i].el.findParent('.form-group', false, true);
25345             if (Roo.bootstrap.version == 3) {
25346                 fg.removeClass([this.invalidClass, this.validClass]);
25347                 fg.addClass(this.validClass);
25348             } else {
25349                 fg.removeClass(['is-valid', 'is-invalid']);
25350                 fg.addClass('is-valid');
25351             }
25352         }
25353     },
25354     
25355      /**
25356      * Mark this field as invalid
25357      * @param {String} msg The validation message
25358      */
25359     markInvalid : function(msg)
25360     {
25361         if(this.allowBlank){
25362             return;
25363         }
25364         
25365         var _this = this;
25366         
25367         this.fireEvent('invalid', this, msg);
25368         
25369         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25370         
25371         if(this.groupId){
25372             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25373         }
25374         
25375         if(label){
25376             label.markInvalid();
25377         }
25378             
25379         if(this.inputType == 'radio'){
25380             
25381             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25382                 var fg = e.findParent('.form-group', false, true);
25383                 if (Roo.bootstrap.version == 3) {
25384                     fg.removeClass([_this.invalidClass, _this.validClass]);
25385                     fg.addClass(_this.invalidClass);
25386                 } else {
25387                     fg.removeClass(['is-invalid', 'is-valid']);
25388                     fg.addClass('is-invalid');
25389                 }
25390             });
25391             
25392             return;
25393         }
25394         
25395         if(!this.groupId){
25396             var fg = this.el.findParent('.form-group', false, true);
25397             if (Roo.bootstrap.version == 3) {
25398                 fg.removeClass([_this.invalidClass, _this.validClass]);
25399                 fg.addClass(_this.invalidClass);
25400             } else {
25401                 fg.removeClass(['is-invalid', 'is-valid']);
25402                 fg.addClass('is-invalid');
25403             }
25404             return;
25405         }
25406         
25407         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25408         
25409         if(!group){
25410             return;
25411         }
25412         
25413         for(var i in group){
25414             var fg = group[i].el.findParent('.form-group', false, true);
25415             if (Roo.bootstrap.version == 3) {
25416                 fg.removeClass([_this.invalidClass, _this.validClass]);
25417                 fg.addClass(_this.invalidClass);
25418             } else {
25419                 fg.removeClass(['is-invalid', 'is-valid']);
25420                 fg.addClass('is-invalid');
25421             }
25422         }
25423         
25424     },
25425     
25426     clearInvalid : function()
25427     {
25428         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25429         
25430         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25431         
25432         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25433         
25434         if (label && label.iconEl) {
25435             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25436             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25437         }
25438     },
25439     
25440     disable : function()
25441     {
25442         if(this.inputType != 'radio'){
25443             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25444             return;
25445         }
25446         
25447         var _this = this;
25448         
25449         if(this.rendered){
25450             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25451                 _this.getActionEl().addClass(this.disabledClass);
25452                 e.dom.disabled = true;
25453             });
25454         }
25455         
25456         this.disabled = true;
25457         this.fireEvent("disable", this);
25458         return this;
25459     },
25460
25461     enable : function()
25462     {
25463         if(this.inputType != 'radio'){
25464             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25465             return;
25466         }
25467         
25468         var _this = this;
25469         
25470         if(this.rendered){
25471             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25472                 _this.getActionEl().removeClass(this.disabledClass);
25473                 e.dom.disabled = false;
25474             });
25475         }
25476         
25477         this.disabled = false;
25478         this.fireEvent("enable", this);
25479         return this;
25480     },
25481     
25482     setBoxLabel : function(v)
25483     {
25484         this.boxLabel = v;
25485         
25486         if(this.rendered){
25487             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25488         }
25489     }
25490
25491 });
25492
25493 Roo.apply(Roo.bootstrap.form.CheckBox, {
25494     
25495     groups: {},
25496     
25497      /**
25498     * register a CheckBox Group
25499     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25500     */
25501     register : function(checkbox)
25502     {
25503         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25504             this.groups[checkbox.groupId] = {};
25505         }
25506         
25507         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25508             return;
25509         }
25510         
25511         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25512         
25513     },
25514     /**
25515     * fetch a CheckBox Group based on the group ID
25516     * @param {string} the group ID
25517     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25518     */
25519     get: function(groupId) {
25520         if (typeof(this.groups[groupId]) == 'undefined') {
25521             return false;
25522         }
25523         
25524         return this.groups[groupId] ;
25525     }
25526     
25527     
25528 });
25529 /*
25530  * - LGPL
25531  *
25532  * RadioItem
25533  * 
25534  */
25535
25536 /**
25537  * @class Roo.bootstrap.form.Radio
25538  * @extends Roo.bootstrap.Component
25539  * Bootstrap Radio class
25540  * @cfg {String} boxLabel - the label associated
25541  * @cfg {String} value - the value of radio
25542  * 
25543  * @constructor
25544  * Create a new Radio
25545  * @param {Object} config The config object
25546  */
25547 Roo.bootstrap.form.Radio = function(config){
25548     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25549     
25550 };
25551
25552 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25553     
25554     boxLabel : '',
25555     
25556     value : '',
25557     
25558     getAutoCreate : function()
25559     {
25560         var cfg = {
25561             tag : 'div',
25562             cls : 'form-group radio',
25563             cn : [
25564                 {
25565                     tag : 'label',
25566                     cls : 'box-label',
25567                     html : this.boxLabel
25568                 }
25569             ]
25570         };
25571         
25572         return cfg;
25573     },
25574     
25575     initEvents : function() 
25576     {
25577         this.parent().register(this);
25578         
25579         this.el.on('click', this.onClick, this);
25580         
25581     },
25582     
25583     onClick : function(e)
25584     {
25585         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25586             this.setChecked(true);
25587         }
25588     },
25589     
25590     setChecked : function(state, suppressEvent)
25591     {
25592         this.parent().setValue(this.value, suppressEvent);
25593         
25594     },
25595     
25596     setBoxLabel : function(v)
25597     {
25598         this.boxLabel = v;
25599         
25600         if(this.rendered){
25601             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25602         }
25603     }
25604     
25605 });
25606  
25607
25608  /*
25609  * - LGPL
25610  *
25611  * Input
25612  * 
25613  */
25614
25615 /**
25616  * @class Roo.bootstrap.form.SecurePass
25617  * @extends Roo.bootstrap.form.Input
25618  * Bootstrap SecurePass class
25619  *
25620  * 
25621  * @constructor
25622  * Create a new SecurePass
25623  * @param {Object} config The config object
25624  */
25625  
25626 Roo.bootstrap.form.SecurePass = function (config) {
25627     // these go here, so the translation tool can replace them..
25628     this.errors = {
25629         PwdEmpty: "Please type a password, and then retype it to confirm.",
25630         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25631         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25632         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25633         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25634         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25635         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25636         TooWeak: "Your password is Too Weak."
25637     },
25638     this.meterLabel = "Password strength:";
25639     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25640     this.meterClass = [
25641         "roo-password-meter-tooweak", 
25642         "roo-password-meter-weak", 
25643         "roo-password-meter-medium", 
25644         "roo-password-meter-strong", 
25645         "roo-password-meter-grey"
25646     ];
25647     
25648     this.errors = {};
25649     
25650     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25651 }
25652
25653 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25654     /**
25655      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25656      * {
25657      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25658      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25659      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25660      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25661      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25662      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25663      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25664      * })
25665      */
25666     // private
25667     
25668     meterWidth: 300,
25669     errorMsg :'',    
25670     errors: false,
25671     imageRoot: '/',
25672     /**
25673      * @cfg {String/Object} Label for the strength meter (defaults to
25674      * 'Password strength:')
25675      */
25676     // private
25677     meterLabel: '',
25678     /**
25679      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25680      * ['Weak', 'Medium', 'Strong'])
25681      */
25682     // private    
25683     pwdStrengths: false,    
25684     // private
25685     strength: 0,
25686     // private
25687     _lastPwd: null,
25688     // private
25689     kCapitalLetter: 0,
25690     kSmallLetter: 1,
25691     kDigit: 2,
25692     kPunctuation: 3,
25693     
25694     insecure: false,
25695     // private
25696     initEvents: function ()
25697     {
25698         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25699
25700         if (this.el.is('input[type=password]') && Roo.isSafari) {
25701             this.el.on('keydown', this.SafariOnKeyDown, this);
25702         }
25703
25704         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25705     },
25706     // private
25707     onRender: function (ct, position)
25708     {
25709         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25710         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25711         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25712
25713         this.trigger.createChild({
25714                    cn: [
25715                     {
25716                     //id: 'PwdMeter',
25717                     tag: 'div',
25718                     cls: 'roo-password-meter-grey col-xs-12',
25719                     style: {
25720                         //width: 0,
25721                         //width: this.meterWidth + 'px'                                                
25722                         }
25723                     },
25724                     {                            
25725                          cls: 'roo-password-meter-text'                          
25726                     }
25727                 ]            
25728         });
25729
25730          
25731         if (this.hideTrigger) {
25732             this.trigger.setDisplayed(false);
25733         }
25734         this.setSize(this.width || '', this.height || '');
25735     },
25736     // private
25737     onDestroy: function ()
25738     {
25739         if (this.trigger) {
25740             this.trigger.removeAllListeners();
25741             this.trigger.remove();
25742         }
25743         if (this.wrap) {
25744             this.wrap.remove();
25745         }
25746         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25747     },
25748     // private
25749     checkStrength: function ()
25750     {
25751         var pwd = this.inputEl().getValue();
25752         if (pwd == this._lastPwd) {
25753             return;
25754         }
25755
25756         var strength;
25757         if (this.ClientSideStrongPassword(pwd)) {
25758             strength = 3;
25759         } else if (this.ClientSideMediumPassword(pwd)) {
25760             strength = 2;
25761         } else if (this.ClientSideWeakPassword(pwd)) {
25762             strength = 1;
25763         } else {
25764             strength = 0;
25765         }
25766         
25767         Roo.log('strength1: ' + strength);
25768         
25769         //var pm = this.trigger.child('div/div/div').dom;
25770         var pm = this.trigger.child('div/div');
25771         pm.removeClass(this.meterClass);
25772         pm.addClass(this.meterClass[strength]);
25773                 
25774         
25775         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25776                 
25777         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25778         
25779         this._lastPwd = pwd;
25780     },
25781     reset: function ()
25782     {
25783         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25784         
25785         this._lastPwd = '';
25786         
25787         var pm = this.trigger.child('div/div');
25788         pm.removeClass(this.meterClass);
25789         pm.addClass('roo-password-meter-grey');        
25790         
25791         
25792         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25793         
25794         pt.innerHTML = '';
25795         this.inputEl().dom.type='password';
25796     },
25797     // private
25798     validateValue: function (value)
25799     {
25800         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25801             return false;
25802         }
25803         if (value.length == 0) {
25804             if (this.allowBlank) {
25805                 this.clearInvalid();
25806                 return true;
25807             }
25808
25809             this.markInvalid(this.errors.PwdEmpty);
25810             this.errorMsg = this.errors.PwdEmpty;
25811             return false;
25812         }
25813         
25814         if(this.insecure){
25815             return true;
25816         }
25817         
25818         if (!value.match(/[\x21-\x7e]+/)) {
25819             this.markInvalid(this.errors.PwdBadChar);
25820             this.errorMsg = this.errors.PwdBadChar;
25821             return false;
25822         }
25823         if (value.length < 6) {
25824             this.markInvalid(this.errors.PwdShort);
25825             this.errorMsg = this.errors.PwdShort;
25826             return false;
25827         }
25828         if (value.length > 16) {
25829             this.markInvalid(this.errors.PwdLong);
25830             this.errorMsg = this.errors.PwdLong;
25831             return false;
25832         }
25833         var strength;
25834         if (this.ClientSideStrongPassword(value)) {
25835             strength = 3;
25836         } else if (this.ClientSideMediumPassword(value)) {
25837             strength = 2;
25838         } else if (this.ClientSideWeakPassword(value)) {
25839             strength = 1;
25840         } else {
25841             strength = 0;
25842         }
25843
25844         
25845         if (strength < 2) {
25846             //this.markInvalid(this.errors.TooWeak);
25847             this.errorMsg = this.errors.TooWeak;
25848             //return false;
25849         }
25850         
25851         
25852         console.log('strength2: ' + strength);
25853         
25854         //var pm = this.trigger.child('div/div/div').dom;
25855         
25856         var pm = this.trigger.child('div/div');
25857         pm.removeClass(this.meterClass);
25858         pm.addClass(this.meterClass[strength]);
25859                 
25860         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25861                 
25862         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25863         
25864         this.errorMsg = ''; 
25865         return true;
25866     },
25867     // private
25868     CharacterSetChecks: function (type)
25869     {
25870         this.type = type;
25871         this.fResult = false;
25872     },
25873     // private
25874     isctype: function (character, type)
25875     {
25876         switch (type) {  
25877             case this.kCapitalLetter:
25878                 if (character >= 'A' && character <= 'Z') {
25879                     return true;
25880                 }
25881                 break;
25882             
25883             case this.kSmallLetter:
25884                 if (character >= 'a' && character <= 'z') {
25885                     return true;
25886                 }
25887                 break;
25888             
25889             case this.kDigit:
25890                 if (character >= '0' && character <= '9') {
25891                     return true;
25892                 }
25893                 break;
25894             
25895             case this.kPunctuation:
25896                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25897                     return true;
25898                 }
25899                 break;
25900             
25901             default:
25902                 return false;
25903         }
25904
25905     },
25906     // private
25907     IsLongEnough: function (pwd, size)
25908     {
25909         return !(pwd == null || isNaN(size) || pwd.length < size);
25910     },
25911     // private
25912     SpansEnoughCharacterSets: function (word, nb)
25913     {
25914         if (!this.IsLongEnough(word, nb))
25915         {
25916             return false;
25917         }
25918
25919         var characterSetChecks = new Array(
25920             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25921             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25922         );
25923         
25924         for (var index = 0; index < word.length; ++index) {
25925             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25926                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25927                     characterSetChecks[nCharSet].fResult = true;
25928                     break;
25929                 }
25930             }
25931         }
25932
25933         var nCharSets = 0;
25934         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25935             if (characterSetChecks[nCharSet].fResult) {
25936                 ++nCharSets;
25937             }
25938         }
25939
25940         if (nCharSets < nb) {
25941             return false;
25942         }
25943         return true;
25944     },
25945     // private
25946     ClientSideStrongPassword: function (pwd)
25947     {
25948         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25949     },
25950     // private
25951     ClientSideMediumPassword: function (pwd)
25952     {
25953         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25954     },
25955     // private
25956     ClientSideWeakPassword: function (pwd)
25957     {
25958         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25959     }
25960           
25961 })//<script type="text/javascript">
25962
25963 /*
25964  * Based  Ext JS Library 1.1.1
25965  * Copyright(c) 2006-2007, Ext JS, LLC.
25966  * LGPL
25967  *
25968  */
25969  
25970 /**
25971  * @class Roo.HtmlEditorCore
25972  * @extends Roo.Component
25973  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25974  *
25975  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25976  */
25977
25978 Roo.HtmlEditorCore = function(config){
25979     
25980     
25981     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25982     
25983     
25984     this.addEvents({
25985         /**
25986          * @event initialize
25987          * Fires when the editor is fully initialized (including the iframe)
25988          * @param {Roo.HtmlEditorCore} this
25989          */
25990         initialize: true,
25991         /**
25992          * @event activate
25993          * Fires when the editor is first receives the focus. Any insertion must wait
25994          * until after this event.
25995          * @param {Roo.HtmlEditorCore} this
25996          */
25997         activate: true,
25998          /**
25999          * @event beforesync
26000          * Fires before the textarea is updated with content from the editor iframe. Return false
26001          * to cancel the sync.
26002          * @param {Roo.HtmlEditorCore} this
26003          * @param {String} html
26004          */
26005         beforesync: true,
26006          /**
26007          * @event beforepush
26008          * Fires before the iframe editor is updated with content from the textarea. Return false
26009          * to cancel the push.
26010          * @param {Roo.HtmlEditorCore} this
26011          * @param {String} html
26012          */
26013         beforepush: true,
26014          /**
26015          * @event sync
26016          * Fires when the textarea is updated with content from the editor iframe.
26017          * @param {Roo.HtmlEditorCore} this
26018          * @param {String} html
26019          */
26020         sync: true,
26021          /**
26022          * @event push
26023          * Fires when the iframe editor is updated with content from the textarea.
26024          * @param {Roo.HtmlEditorCore} this
26025          * @param {String} html
26026          */
26027         push: true,
26028         
26029         /**
26030          * @event editorevent
26031          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26032          * @param {Roo.HtmlEditorCore} this
26033          */
26034         editorevent: true
26035         
26036     });
26037     
26038     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26039     
26040     // defaults : white / black...
26041     this.applyBlacklists();
26042     
26043     
26044     
26045 };
26046
26047
26048 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
26049
26050
26051      /**
26052      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
26053      */
26054     
26055     owner : false,
26056     
26057      /**
26058      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26059      *                        Roo.resizable.
26060      */
26061     resizable : false,
26062      /**
26063      * @cfg {Number} height (in pixels)
26064      */   
26065     height: 300,
26066    /**
26067      * @cfg {Number} width (in pixels)
26068      */   
26069     width: 500,
26070     
26071     /**
26072      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26073      * 
26074      */
26075     stylesheets: false,
26076     
26077     /**
26078      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26079      */
26080     allowComments: false,
26081     // id of frame..
26082     frameId: false,
26083     
26084     // private properties
26085     validationEvent : false,
26086     deferHeight: true,
26087     initialized : false,
26088     activated : false,
26089     sourceEditMode : false,
26090     onFocus : Roo.emptyFn,
26091     iframePad:3,
26092     hideMode:'offsets',
26093     
26094     clearUp: true,
26095     
26096     // blacklist + whitelisted elements..
26097     black: false,
26098     white: false,
26099      
26100     bodyCls : '',
26101
26102     /**
26103      * Protected method that will not generally be called directly. It
26104      * is called when the editor initializes the iframe with HTML contents. Override this method if you
26105      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26106      */
26107     getDocMarkup : function(){
26108         // body styles..
26109         var st = '';
26110         
26111         // inherit styels from page...?? 
26112         if (this.stylesheets === false) {
26113             
26114             Roo.get(document.head).select('style').each(function(node) {
26115                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26116             });
26117             
26118             Roo.get(document.head).select('link').each(function(node) { 
26119                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26120             });
26121             
26122         } else if (!this.stylesheets.length) {
26123                 // simple..
26124                 st = '<style type="text/css">' +
26125                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26126                    '</style>';
26127         } else {
26128             for (var i in this.stylesheets) { 
26129                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26130             }
26131             
26132         }
26133         
26134         st +=  '<style type="text/css">' +
26135             'IMG { cursor: pointer } ' +
26136         '</style>';
26137
26138         var cls = 'roo-htmleditor-body';
26139         
26140         if(this.bodyCls.length){
26141             cls += ' ' + this.bodyCls;
26142         }
26143         
26144         return '<html><head>' + st  +
26145             //<style type="text/css">' +
26146             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26147             //'</style>' +
26148             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
26149     },
26150
26151     // private
26152     onRender : function(ct, position)
26153     {
26154         var _t = this;
26155         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26156         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26157         
26158         
26159         this.el.dom.style.border = '0 none';
26160         this.el.dom.setAttribute('tabIndex', -1);
26161         this.el.addClass('x-hidden hide');
26162         
26163         
26164         
26165         if(Roo.isIE){ // fix IE 1px bogus margin
26166             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26167         }
26168        
26169         
26170         this.frameId = Roo.id();
26171         
26172          
26173         
26174         var iframe = this.owner.wrap.createChild({
26175             tag: 'iframe',
26176             cls: 'form-control', // bootstrap..
26177             id: this.frameId,
26178             name: this.frameId,
26179             frameBorder : 'no',
26180             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
26181         }, this.el
26182         );
26183         
26184         
26185         this.iframe = iframe.dom;
26186
26187          this.assignDocWin();
26188         
26189         this.doc.designMode = 'on';
26190        
26191         this.doc.open();
26192         this.doc.write(this.getDocMarkup());
26193         this.doc.close();
26194
26195         
26196         var task = { // must defer to wait for browser to be ready
26197             run : function(){
26198                 //console.log("run task?" + this.doc.readyState);
26199                 this.assignDocWin();
26200                 if(this.doc.body || this.doc.readyState == 'complete'){
26201                     try {
26202                         this.doc.designMode="on";
26203                     } catch (e) {
26204                         return;
26205                     }
26206                     Roo.TaskMgr.stop(task);
26207                     this.initEditor.defer(10, this);
26208                 }
26209             },
26210             interval : 10,
26211             duration: 10000,
26212             scope: this
26213         };
26214         Roo.TaskMgr.start(task);
26215
26216     },
26217
26218     // private
26219     onResize : function(w, h)
26220     {
26221          Roo.log('resize: ' +w + ',' + h );
26222         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26223         if(!this.iframe){
26224             return;
26225         }
26226         if(typeof w == 'number'){
26227             
26228             this.iframe.style.width = w + 'px';
26229         }
26230         if(typeof h == 'number'){
26231             
26232             this.iframe.style.height = h + 'px';
26233             if(this.doc){
26234                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26235             }
26236         }
26237         
26238     },
26239
26240     /**
26241      * Toggles the editor between standard and source edit mode.
26242      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26243      */
26244     toggleSourceEdit : function(sourceEditMode){
26245         
26246         this.sourceEditMode = sourceEditMode === true;
26247         
26248         if(this.sourceEditMode){
26249  
26250             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
26251             
26252         }else{
26253             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26254             //this.iframe.className = '';
26255             this.deferFocus();
26256         }
26257         //this.setSize(this.owner.wrap.getSize());
26258         //this.fireEvent('editmodechange', this, this.sourceEditMode);
26259     },
26260
26261     
26262   
26263
26264     /**
26265      * Protected method that will not generally be called directly. If you need/want
26266      * custom HTML cleanup, this is the method you should override.
26267      * @param {String} html The HTML to be cleaned
26268      * return {String} The cleaned HTML
26269      */
26270     cleanHtml : function(html){
26271         html = String(html);
26272         if(html.length > 5){
26273             if(Roo.isSafari){ // strip safari nonsense
26274                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26275             }
26276         }
26277         if(html == '&nbsp;'){
26278             html = '';
26279         }
26280         return html;
26281     },
26282
26283     /**
26284      * HTML Editor -> Textarea
26285      * Protected method that will not generally be called directly. Syncs the contents
26286      * of the editor iframe with the textarea.
26287      */
26288     syncValue : function(){
26289         if(this.initialized){
26290             var bd = (this.doc.body || this.doc.documentElement);
26291             //this.cleanUpPaste(); -- this is done else where and causes havoc..
26292             var html = bd.innerHTML;
26293             if(Roo.isSafari){
26294                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26295                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26296                 if(m && m[1]){
26297                     html = '<div style="'+m[0]+'">' + html + '</div>';
26298                 }
26299             }
26300             html = this.cleanHtml(html);
26301             // fix up the special chars.. normaly like back quotes in word...
26302             // however we do not want to do this with chinese..
26303             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26304                 
26305                 var cc = match.charCodeAt();
26306
26307                 // Get the character value, handling surrogate pairs
26308                 if (match.length == 2) {
26309                     // It's a surrogate pair, calculate the Unicode code point
26310                     var high = match.charCodeAt(0) - 0xD800;
26311                     var low  = match.charCodeAt(1) - 0xDC00;
26312                     cc = (high * 0x400) + low + 0x10000;
26313                 }  else if (
26314                     (cc >= 0x4E00 && cc < 0xA000 ) ||
26315                     (cc >= 0x3400 && cc < 0x4E00 ) ||
26316                     (cc >= 0xf900 && cc < 0xfb00 )
26317                 ) {
26318                         return match;
26319                 }  
26320          
26321                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26322                 return "&#" + cc + ";";
26323                 
26324                 
26325             });
26326             
26327             
26328              
26329             if(this.owner.fireEvent('beforesync', this, html) !== false){
26330                 this.el.dom.value = html;
26331                 this.owner.fireEvent('sync', this, html);
26332             }
26333         }
26334     },
26335
26336     /**
26337      * Protected method that will not generally be called directly. Pushes the value of the textarea
26338      * into the iframe editor.
26339      */
26340     pushValue : function(){
26341         if(this.initialized){
26342             var v = this.el.dom.value.trim();
26343             
26344 //            if(v.length < 1){
26345 //                v = '&#160;';
26346 //            }
26347             
26348             if(this.owner.fireEvent('beforepush', this, v) !== false){
26349                 var d = (this.doc.body || this.doc.documentElement);
26350                 d.innerHTML = v;
26351                 this.cleanUpPaste();
26352                 this.el.dom.value = d.innerHTML;
26353                 this.owner.fireEvent('push', this, v);
26354             }
26355         }
26356     },
26357
26358     // private
26359     deferFocus : function(){
26360         this.focus.defer(10, this);
26361     },
26362
26363     // doc'ed in Field
26364     focus : function(){
26365         if(this.win && !this.sourceEditMode){
26366             this.win.focus();
26367         }else{
26368             this.el.focus();
26369         }
26370     },
26371     
26372     assignDocWin: function()
26373     {
26374         var iframe = this.iframe;
26375         
26376          if(Roo.isIE){
26377             this.doc = iframe.contentWindow.document;
26378             this.win = iframe.contentWindow;
26379         } else {
26380 //            if (!Roo.get(this.frameId)) {
26381 //                return;
26382 //            }
26383 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26384 //            this.win = Roo.get(this.frameId).dom.contentWindow;
26385             
26386             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26387                 return;
26388             }
26389             
26390             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26391             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26392         }
26393     },
26394     
26395     // private
26396     initEditor : function(){
26397         //console.log("INIT EDITOR");
26398         this.assignDocWin();
26399         
26400         
26401         
26402         this.doc.designMode="on";
26403         this.doc.open();
26404         this.doc.write(this.getDocMarkup());
26405         this.doc.close();
26406         
26407         var dbody = (this.doc.body || this.doc.documentElement);
26408         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26409         // this copies styles from the containing element into thsi one..
26410         // not sure why we need all of this..
26411         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26412         
26413         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26414         //ss['background-attachment'] = 'fixed'; // w3c
26415         dbody.bgProperties = 'fixed'; // ie
26416         //Roo.DomHelper.applyStyles(dbody, ss);
26417         Roo.EventManager.on(this.doc, {
26418             //'mousedown': this.onEditorEvent,
26419             'mouseup': this.onEditorEvent,
26420             'dblclick': this.onEditorEvent,
26421             'click': this.onEditorEvent,
26422             'keyup': this.onEditorEvent,
26423             buffer:100,
26424             scope: this
26425         });
26426         if(Roo.isGecko){
26427             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26428         }
26429         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26430             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26431         }
26432         this.initialized = true;
26433
26434         this.owner.fireEvent('initialize', this);
26435         this.pushValue();
26436     },
26437
26438     // private
26439     onDestroy : function(){
26440         
26441         
26442         
26443         if(this.rendered){
26444             
26445             //for (var i =0; i < this.toolbars.length;i++) {
26446             //    // fixme - ask toolbars for heights?
26447             //    this.toolbars[i].onDestroy();
26448            // }
26449             
26450             //this.wrap.dom.innerHTML = '';
26451             //this.wrap.remove();
26452         }
26453     },
26454
26455     // private
26456     onFirstFocus : function(){
26457         
26458         this.assignDocWin();
26459         
26460         
26461         this.activated = true;
26462          
26463     
26464         if(Roo.isGecko){ // prevent silly gecko errors
26465             this.win.focus();
26466             var s = this.win.getSelection();
26467             if(!s.focusNode || s.focusNode.nodeType != 3){
26468                 var r = s.getRangeAt(0);
26469                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26470                 r.collapse(true);
26471                 this.deferFocus();
26472             }
26473             try{
26474                 this.execCmd('useCSS', true);
26475                 this.execCmd('styleWithCSS', false);
26476             }catch(e){}
26477         }
26478         this.owner.fireEvent('activate', this);
26479     },
26480
26481     // private
26482     adjustFont: function(btn){
26483         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26484         //if(Roo.isSafari){ // safari
26485         //    adjust *= 2;
26486        // }
26487         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26488         if(Roo.isSafari){ // safari
26489             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26490             v =  (v < 10) ? 10 : v;
26491             v =  (v > 48) ? 48 : v;
26492             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26493             
26494         }
26495         
26496         
26497         v = Math.max(1, v+adjust);
26498         
26499         this.execCmd('FontSize', v  );
26500     },
26501
26502     onEditorEvent : function(e)
26503     {
26504         this.owner.fireEvent('editorevent', this, e);
26505       //  this.updateToolbar();
26506         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26507     },
26508
26509     insertTag : function(tg)
26510     {
26511         // could be a bit smarter... -> wrap the current selected tRoo..
26512         if (tg.toLowerCase() == 'span' ||
26513             tg.toLowerCase() == 'code' ||
26514             tg.toLowerCase() == 'sup' ||
26515             tg.toLowerCase() == 'sub' 
26516             ) {
26517             
26518             range = this.createRange(this.getSelection());
26519             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26520             wrappingNode.appendChild(range.extractContents());
26521             range.insertNode(wrappingNode);
26522
26523             return;
26524             
26525             
26526             
26527         }
26528         this.execCmd("formatblock",   tg);
26529         
26530     },
26531     
26532     insertText : function(txt)
26533     {
26534         
26535         
26536         var range = this.createRange();
26537         range.deleteContents();
26538                //alert(Sender.getAttribute('label'));
26539                
26540         range.insertNode(this.doc.createTextNode(txt));
26541     } ,
26542     
26543      
26544
26545     /**
26546      * Executes a Midas editor command on the editor document and performs necessary focus and
26547      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26548      * @param {String} cmd The Midas command
26549      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26550      */
26551     relayCmd : function(cmd, value){
26552         this.win.focus();
26553         this.execCmd(cmd, value);
26554         this.owner.fireEvent('editorevent', this);
26555         //this.updateToolbar();
26556         this.owner.deferFocus();
26557     },
26558
26559     /**
26560      * Executes a Midas editor command directly on the editor document.
26561      * For visual commands, you should use {@link #relayCmd} instead.
26562      * <b>This should only be called after the editor is initialized.</b>
26563      * @param {String} cmd The Midas command
26564      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26565      */
26566     execCmd : function(cmd, value){
26567         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26568         this.syncValue();
26569     },
26570  
26571  
26572    
26573     /**
26574      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26575      * to insert tRoo.
26576      * @param {String} text | dom node.. 
26577      */
26578     insertAtCursor : function(text)
26579     {
26580         
26581         if(!this.activated){
26582             return;
26583         }
26584         /*
26585         if(Roo.isIE){
26586             this.win.focus();
26587             var r = this.doc.selection.createRange();
26588             if(r){
26589                 r.collapse(true);
26590                 r.pasteHTML(text);
26591                 this.syncValue();
26592                 this.deferFocus();
26593             
26594             }
26595             return;
26596         }
26597         */
26598         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26599             this.win.focus();
26600             
26601             
26602             // from jquery ui (MIT licenced)
26603             var range, node;
26604             var win = this.win;
26605             
26606             if (win.getSelection && win.getSelection().getRangeAt) {
26607                 range = win.getSelection().getRangeAt(0);
26608                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26609                 range.insertNode(node);
26610             } else if (win.document.selection && win.document.selection.createRange) {
26611                 // no firefox support
26612                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26613                 win.document.selection.createRange().pasteHTML(txt);
26614             } else {
26615                 // no firefox support
26616                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26617                 this.execCmd('InsertHTML', txt);
26618             } 
26619             
26620             this.syncValue();
26621             
26622             this.deferFocus();
26623         }
26624     },
26625  // private
26626     mozKeyPress : function(e){
26627         if(e.ctrlKey){
26628             var c = e.getCharCode(), cmd;
26629           
26630             if(c > 0){
26631                 c = String.fromCharCode(c).toLowerCase();
26632                 switch(c){
26633                     case 'b':
26634                         cmd = 'bold';
26635                         break;
26636                     case 'i':
26637                         cmd = 'italic';
26638                         break;
26639                     
26640                     case 'u':
26641                         cmd = 'underline';
26642                         break;
26643                     
26644                     case 'v':
26645                         this.cleanUpPaste.defer(100, this);
26646                         return;
26647                         
26648                 }
26649                 if(cmd){
26650                     this.win.focus();
26651                     this.execCmd(cmd);
26652                     this.deferFocus();
26653                     e.preventDefault();
26654                 }
26655                 
26656             }
26657         }
26658     },
26659
26660     // private
26661     fixKeys : function(){ // load time branching for fastest keydown performance
26662         if(Roo.isIE){
26663             return function(e){
26664                 var k = e.getKey(), r;
26665                 if(k == e.TAB){
26666                     e.stopEvent();
26667                     r = this.doc.selection.createRange();
26668                     if(r){
26669                         r.collapse(true);
26670                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26671                         this.deferFocus();
26672                     }
26673                     return;
26674                 }
26675                 
26676                 if(k == e.ENTER){
26677                     r = this.doc.selection.createRange();
26678                     if(r){
26679                         var target = r.parentElement();
26680                         if(!target || target.tagName.toLowerCase() != 'li'){
26681                             e.stopEvent();
26682                             r.pasteHTML('<br />');
26683                             r.collapse(false);
26684                             r.select();
26685                         }
26686                     }
26687                 }
26688                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26689                     this.cleanUpPaste.defer(100, this);
26690                     return;
26691                 }
26692                 
26693                 
26694             };
26695         }else if(Roo.isOpera){
26696             return function(e){
26697                 var k = e.getKey();
26698                 if(k == e.TAB){
26699                     e.stopEvent();
26700                     this.win.focus();
26701                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26702                     this.deferFocus();
26703                 }
26704                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26705                     this.cleanUpPaste.defer(100, this);
26706                     return;
26707                 }
26708                 
26709             };
26710         }else if(Roo.isSafari){
26711             return function(e){
26712                 var k = e.getKey();
26713                 
26714                 if(k == e.TAB){
26715                     e.stopEvent();
26716                     this.execCmd('InsertText','\t');
26717                     this.deferFocus();
26718                     return;
26719                 }
26720                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26721                     this.cleanUpPaste.defer(100, this);
26722                     return;
26723                 }
26724                 
26725              };
26726         }
26727     }(),
26728     
26729     getAllAncestors: function()
26730     {
26731         var p = this.getSelectedNode();
26732         var a = [];
26733         if (!p) {
26734             a.push(p); // push blank onto stack..
26735             p = this.getParentElement();
26736         }
26737         
26738         
26739         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26740             a.push(p);
26741             p = p.parentNode;
26742         }
26743         a.push(this.doc.body);
26744         return a;
26745     },
26746     lastSel : false,
26747     lastSelNode : false,
26748     
26749     
26750     getSelection : function() 
26751     {
26752         this.assignDocWin();
26753         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26754     },
26755     
26756     getSelectedNode: function() 
26757     {
26758         // this may only work on Gecko!!!
26759         
26760         // should we cache this!!!!
26761         
26762         
26763         
26764          
26765         var range = this.createRange(this.getSelection()).cloneRange();
26766         
26767         if (Roo.isIE) {
26768             var parent = range.parentElement();
26769             while (true) {
26770                 var testRange = range.duplicate();
26771                 testRange.moveToElementText(parent);
26772                 if (testRange.inRange(range)) {
26773                     break;
26774                 }
26775                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26776                     break;
26777                 }
26778                 parent = parent.parentElement;
26779             }
26780             return parent;
26781         }
26782         
26783         // is ancestor a text element.
26784         var ac =  range.commonAncestorContainer;
26785         if (ac.nodeType == 3) {
26786             ac = ac.parentNode;
26787         }
26788         
26789         var ar = ac.childNodes;
26790          
26791         var nodes = [];
26792         var other_nodes = [];
26793         var has_other_nodes = false;
26794         for (var i=0;i<ar.length;i++) {
26795             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26796                 continue;
26797             }
26798             // fullly contained node.
26799             
26800             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26801                 nodes.push(ar[i]);
26802                 continue;
26803             }
26804             
26805             // probably selected..
26806             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26807                 other_nodes.push(ar[i]);
26808                 continue;
26809             }
26810             // outer..
26811             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26812                 continue;
26813             }
26814             
26815             
26816             has_other_nodes = true;
26817         }
26818         if (!nodes.length && other_nodes.length) {
26819             nodes= other_nodes;
26820         }
26821         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26822             return false;
26823         }
26824         
26825         return nodes[0];
26826     },
26827     createRange: function(sel)
26828     {
26829         // this has strange effects when using with 
26830         // top toolbar - not sure if it's a great idea.
26831         //this.editor.contentWindow.focus();
26832         if (typeof sel != "undefined") {
26833             try {
26834                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26835             } catch(e) {
26836                 return this.doc.createRange();
26837             }
26838         } else {
26839             return this.doc.createRange();
26840         }
26841     },
26842     getParentElement: function()
26843     {
26844         
26845         this.assignDocWin();
26846         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26847         
26848         var range = this.createRange(sel);
26849          
26850         try {
26851             var p = range.commonAncestorContainer;
26852             while (p.nodeType == 3) { // text node
26853                 p = p.parentNode;
26854             }
26855             return p;
26856         } catch (e) {
26857             return null;
26858         }
26859     
26860     },
26861     /***
26862      *
26863      * Range intersection.. the hard stuff...
26864      *  '-1' = before
26865      *  '0' = hits..
26866      *  '1' = after.
26867      *         [ -- selected range --- ]
26868      *   [fail]                        [fail]
26869      *
26870      *    basically..
26871      *      if end is before start or  hits it. fail.
26872      *      if start is after end or hits it fail.
26873      *
26874      *   if either hits (but other is outside. - then it's not 
26875      *   
26876      *    
26877      **/
26878     
26879     
26880     // @see http://www.thismuchiknow.co.uk/?p=64.
26881     rangeIntersectsNode : function(range, node)
26882     {
26883         var nodeRange = node.ownerDocument.createRange();
26884         try {
26885             nodeRange.selectNode(node);
26886         } catch (e) {
26887             nodeRange.selectNodeContents(node);
26888         }
26889     
26890         var rangeStartRange = range.cloneRange();
26891         rangeStartRange.collapse(true);
26892     
26893         var rangeEndRange = range.cloneRange();
26894         rangeEndRange.collapse(false);
26895     
26896         var nodeStartRange = nodeRange.cloneRange();
26897         nodeStartRange.collapse(true);
26898     
26899         var nodeEndRange = nodeRange.cloneRange();
26900         nodeEndRange.collapse(false);
26901     
26902         return rangeStartRange.compareBoundaryPoints(
26903                  Range.START_TO_START, nodeEndRange) == -1 &&
26904                rangeEndRange.compareBoundaryPoints(
26905                  Range.START_TO_START, nodeStartRange) == 1;
26906         
26907          
26908     },
26909     rangeCompareNode : function(range, node)
26910     {
26911         var nodeRange = node.ownerDocument.createRange();
26912         try {
26913             nodeRange.selectNode(node);
26914         } catch (e) {
26915             nodeRange.selectNodeContents(node);
26916         }
26917         
26918         
26919         range.collapse(true);
26920     
26921         nodeRange.collapse(true);
26922      
26923         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26924         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26925          
26926         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26927         
26928         var nodeIsBefore   =  ss == 1;
26929         var nodeIsAfter    = ee == -1;
26930         
26931         if (nodeIsBefore && nodeIsAfter) {
26932             return 0; // outer
26933         }
26934         if (!nodeIsBefore && nodeIsAfter) {
26935             return 1; //right trailed.
26936         }
26937         
26938         if (nodeIsBefore && !nodeIsAfter) {
26939             return 2;  // left trailed.
26940         }
26941         // fully contined.
26942         return 3;
26943     },
26944
26945     // private? - in a new class?
26946     cleanUpPaste :  function()
26947     {
26948         // cleans up the whole document..
26949         Roo.log('cleanuppaste');
26950         
26951         this.cleanUpChildren(this.doc.body);
26952         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26953         if (clean != this.doc.body.innerHTML) {
26954             this.doc.body.innerHTML = clean;
26955         }
26956         
26957     },
26958     
26959     cleanWordChars : function(input) {// change the chars to hex code
26960         var he = Roo.HtmlEditorCore;
26961         
26962         var output = input;
26963         Roo.each(he.swapCodes, function(sw) { 
26964             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26965             
26966             output = output.replace(swapper, sw[1]);
26967         });
26968         
26969         return output;
26970     },
26971     
26972     
26973     cleanUpChildren : function (n)
26974     {
26975         if (!n.childNodes.length) {
26976             return;
26977         }
26978         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26979            this.cleanUpChild(n.childNodes[i]);
26980         }
26981     },
26982     
26983     
26984         
26985     
26986     cleanUpChild : function (node)
26987     {
26988         var ed = this;
26989         //console.log(node);
26990         if (node.nodeName == "#text") {
26991             // clean up silly Windows -- stuff?
26992             return; 
26993         }
26994         if (node.nodeName == "#comment") {
26995             if (!this.allowComments) {
26996                 node.parentNode.removeChild(node);
26997             }
26998             // clean up silly Windows -- stuff?
26999             return; 
27000         }
27001         var lcname = node.tagName.toLowerCase();
27002         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
27003         // whitelist of tags..
27004         
27005         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
27006             // remove node.
27007             node.parentNode.removeChild(node);
27008             return;
27009             
27010         }
27011         
27012         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27013         
27014         // spans with no attributes - just remove them..
27015         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
27016             remove_keep_children = true;
27017         }
27018         
27019         // remove <a name=....> as rendering on yahoo mailer is borked with this.
27020         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27021         
27022         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27023         //    remove_keep_children = true;
27024         //}
27025         
27026         if (remove_keep_children) {
27027             this.cleanUpChildren(node);
27028             // inserts everything just before this node...
27029             while (node.childNodes.length) {
27030                 var cn = node.childNodes[0];
27031                 node.removeChild(cn);
27032                 node.parentNode.insertBefore(cn, node);
27033             }
27034             node.parentNode.removeChild(node);
27035             return;
27036         }
27037         
27038         if (!node.attributes || !node.attributes.length) {
27039             
27040           
27041             
27042             
27043             this.cleanUpChildren(node);
27044             return;
27045         }
27046         
27047         function cleanAttr(n,v)
27048         {
27049             
27050             if (v.match(/^\./) || v.match(/^\//)) {
27051                 return;
27052             }
27053             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27054                 return;
27055             }
27056             if (v.match(/^#/)) {
27057                 return;
27058             }
27059             if (v.match(/^\{/)) { // allow template editing.
27060                 return;
27061             }
27062 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27063             node.removeAttribute(n);
27064             
27065         }
27066         
27067         var cwhite = this.cwhite;
27068         var cblack = this.cblack;
27069             
27070         function cleanStyle(n,v)
27071         {
27072             if (v.match(/expression/)) { //XSS?? should we even bother..
27073                 node.removeAttribute(n);
27074                 return;
27075             }
27076             
27077             var parts = v.split(/;/);
27078             var clean = [];
27079             
27080             Roo.each(parts, function(p) {
27081                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27082                 if (!p.length) {
27083                     return true;
27084                 }
27085                 var l = p.split(':').shift().replace(/\s+/g,'');
27086                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27087                 
27088                 if ( cwhite.length && cblack.indexOf(l) > -1) {
27089 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27090                     //node.removeAttribute(n);
27091                     return true;
27092                 }
27093                 //Roo.log()
27094                 // only allow 'c whitelisted system attributes'
27095                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
27096 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27097                     //node.removeAttribute(n);
27098                     return true;
27099                 }
27100                 
27101                 
27102                  
27103                 
27104                 clean.push(p);
27105                 return true;
27106             });
27107             if (clean.length) { 
27108                 node.setAttribute(n, clean.join(';'));
27109             } else {
27110                 node.removeAttribute(n);
27111             }
27112             
27113         }
27114         
27115         
27116         for (var i = node.attributes.length-1; i > -1 ; i--) {
27117             var a = node.attributes[i];
27118             //console.log(a);
27119             
27120             if (a.name.toLowerCase().substr(0,2)=='on')  {
27121                 node.removeAttribute(a.name);
27122                 continue;
27123             }
27124             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27125                 node.removeAttribute(a.name);
27126                 continue;
27127             }
27128             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27129                 cleanAttr(a.name,a.value); // fixme..
27130                 continue;
27131             }
27132             if (a.name == 'style') {
27133                 cleanStyle(a.name,a.value);
27134                 continue;
27135             }
27136             /// clean up MS crap..
27137             // tecnically this should be a list of valid class'es..
27138             
27139             
27140             if (a.name == 'class') {
27141                 if (a.value.match(/^Mso/)) {
27142                     node.removeAttribute('class');
27143                 }
27144                 
27145                 if (a.value.match(/^body$/)) {
27146                     node.removeAttribute('class');
27147                 }
27148                 continue;
27149             }
27150             
27151             // style cleanup!?
27152             // class cleanup?
27153             
27154         }
27155         
27156         
27157         this.cleanUpChildren(node);
27158         
27159         
27160     },
27161     
27162     /**
27163      * Clean up MS wordisms...
27164      */
27165     cleanWord : function(node)
27166     {
27167         if (!node) {
27168             this.cleanWord(this.doc.body);
27169             return;
27170         }
27171         
27172         if(
27173                 node.nodeName == 'SPAN' &&
27174                 !node.hasAttributes() &&
27175                 node.childNodes.length == 1 &&
27176                 node.firstChild.nodeName == "#text"  
27177         ) {
27178             var textNode = node.firstChild;
27179             node.removeChild(textNode);
27180             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27181                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27182             }
27183             node.parentNode.insertBefore(textNode, node);
27184             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27185                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27186             }
27187             node.parentNode.removeChild(node);
27188         }
27189         
27190         if (node.nodeName == "#text") {
27191             // clean up silly Windows -- stuff?
27192             return; 
27193         }
27194         if (node.nodeName == "#comment") {
27195             node.parentNode.removeChild(node);
27196             // clean up silly Windows -- stuff?
27197             return; 
27198         }
27199         
27200         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27201             node.parentNode.removeChild(node);
27202             return;
27203         }
27204         //Roo.log(node.tagName);
27205         // remove - but keep children..
27206         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27207             //Roo.log('-- removed');
27208             while (node.childNodes.length) {
27209                 var cn = node.childNodes[0];
27210                 node.removeChild(cn);
27211                 node.parentNode.insertBefore(cn, node);
27212                 // move node to parent - and clean it..
27213                 this.cleanWord(cn);
27214             }
27215             node.parentNode.removeChild(node);
27216             /// no need to iterate chidlren = it's got none..
27217             //this.iterateChildren(node, this.cleanWord);
27218             return;
27219         }
27220         // clean styles
27221         if (node.className.length) {
27222             
27223             var cn = node.className.split(/\W+/);
27224             var cna = [];
27225             Roo.each(cn, function(cls) {
27226                 if (cls.match(/Mso[a-zA-Z]+/)) {
27227                     return;
27228                 }
27229                 cna.push(cls);
27230             });
27231             node.className = cna.length ? cna.join(' ') : '';
27232             if (!cna.length) {
27233                 node.removeAttribute("class");
27234             }
27235         }
27236         
27237         if (node.hasAttribute("lang")) {
27238             node.removeAttribute("lang");
27239         }
27240         
27241         if (node.hasAttribute("style")) {
27242             
27243             var styles = node.getAttribute("style").split(";");
27244             var nstyle = [];
27245             Roo.each(styles, function(s) {
27246                 if (!s.match(/:/)) {
27247                     return;
27248                 }
27249                 var kv = s.split(":");
27250                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27251                     return;
27252                 }
27253                 // what ever is left... we allow.
27254                 nstyle.push(s);
27255             });
27256             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27257             if (!nstyle.length) {
27258                 node.removeAttribute('style');
27259             }
27260         }
27261         this.iterateChildren(node, this.cleanWord);
27262         
27263         
27264         
27265     },
27266     /**
27267      * iterateChildren of a Node, calling fn each time, using this as the scole..
27268      * @param {DomNode} node node to iterate children of.
27269      * @param {Function} fn method of this class to call on each item.
27270      */
27271     iterateChildren : function(node, fn)
27272     {
27273         if (!node.childNodes.length) {
27274                 return;
27275         }
27276         for (var i = node.childNodes.length-1; i > -1 ; i--) {
27277            fn.call(this, node.childNodes[i])
27278         }
27279     },
27280     
27281     
27282     /**
27283      * cleanTableWidths.
27284      *
27285      * Quite often pasting from word etc.. results in tables with column and widths.
27286      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27287      *
27288      */
27289     cleanTableWidths : function(node)
27290     {
27291          
27292          
27293         if (!node) {
27294             this.cleanTableWidths(this.doc.body);
27295             return;
27296         }
27297         
27298         // ignore list...
27299         if (node.nodeName == "#text" || node.nodeName == "#comment") {
27300             return; 
27301         }
27302         Roo.log(node.tagName);
27303         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27304             this.iterateChildren(node, this.cleanTableWidths);
27305             return;
27306         }
27307         if (node.hasAttribute('width')) {
27308             node.removeAttribute('width');
27309         }
27310         
27311          
27312         if (node.hasAttribute("style")) {
27313             // pretty basic...
27314             
27315             var styles = node.getAttribute("style").split(";");
27316             var nstyle = [];
27317             Roo.each(styles, function(s) {
27318                 if (!s.match(/:/)) {
27319                     return;
27320                 }
27321                 var kv = s.split(":");
27322                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27323                     return;
27324                 }
27325                 // what ever is left... we allow.
27326                 nstyle.push(s);
27327             });
27328             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27329             if (!nstyle.length) {
27330                 node.removeAttribute('style');
27331             }
27332         }
27333         
27334         this.iterateChildren(node, this.cleanTableWidths);
27335         
27336         
27337     },
27338     
27339     
27340     
27341     
27342     domToHTML : function(currentElement, depth, nopadtext) {
27343         
27344         depth = depth || 0;
27345         nopadtext = nopadtext || false;
27346     
27347         if (!currentElement) {
27348             return this.domToHTML(this.doc.body);
27349         }
27350         
27351         //Roo.log(currentElement);
27352         var j;
27353         var allText = false;
27354         var nodeName = currentElement.nodeName;
27355         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27356         
27357         if  (nodeName == '#text') {
27358             
27359             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27360         }
27361         
27362         
27363         var ret = '';
27364         if (nodeName != 'BODY') {
27365              
27366             var i = 0;
27367             // Prints the node tagName, such as <A>, <IMG>, etc
27368             if (tagName) {
27369                 var attr = [];
27370                 for(i = 0; i < currentElement.attributes.length;i++) {
27371                     // quoting?
27372                     var aname = currentElement.attributes.item(i).name;
27373                     if (!currentElement.attributes.item(i).value.length) {
27374                         continue;
27375                     }
27376                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27377                 }
27378                 
27379                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27380             } 
27381             else {
27382                 
27383                 // eack
27384             }
27385         } else {
27386             tagName = false;
27387         }
27388         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27389             return ret;
27390         }
27391         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27392             nopadtext = true;
27393         }
27394         
27395         
27396         // Traverse the tree
27397         i = 0;
27398         var currentElementChild = currentElement.childNodes.item(i);
27399         var allText = true;
27400         var innerHTML  = '';
27401         lastnode = '';
27402         while (currentElementChild) {
27403             // Formatting code (indent the tree so it looks nice on the screen)
27404             var nopad = nopadtext;
27405             if (lastnode == 'SPAN') {
27406                 nopad  = true;
27407             }
27408             // text
27409             if  (currentElementChild.nodeName == '#text') {
27410                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27411                 toadd = nopadtext ? toadd : toadd.trim();
27412                 if (!nopad && toadd.length > 80) {
27413                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
27414                 }
27415                 innerHTML  += toadd;
27416                 
27417                 i++;
27418                 currentElementChild = currentElement.childNodes.item(i);
27419                 lastNode = '';
27420                 continue;
27421             }
27422             allText = false;
27423             
27424             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
27425                 
27426             // Recursively traverse the tree structure of the child node
27427             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
27428             lastnode = currentElementChild.nodeName;
27429             i++;
27430             currentElementChild=currentElement.childNodes.item(i);
27431         }
27432         
27433         ret += innerHTML;
27434         
27435         if (!allText) {
27436                 // The remaining code is mostly for formatting the tree
27437             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27438         }
27439         
27440         
27441         if (tagName) {
27442             ret+= "</"+tagName+">";
27443         }
27444         return ret;
27445         
27446     },
27447         
27448     applyBlacklists : function()
27449     {
27450         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27451         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27452         
27453         this.white = [];
27454         this.black = [];
27455         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27456             if (b.indexOf(tag) > -1) {
27457                 return;
27458             }
27459             this.white.push(tag);
27460             
27461         }, this);
27462         
27463         Roo.each(w, function(tag) {
27464             if (b.indexOf(tag) > -1) {
27465                 return;
27466             }
27467             if (this.white.indexOf(tag) > -1) {
27468                 return;
27469             }
27470             this.white.push(tag);
27471             
27472         }, this);
27473         
27474         
27475         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27476             if (w.indexOf(tag) > -1) {
27477                 return;
27478             }
27479             this.black.push(tag);
27480             
27481         }, this);
27482         
27483         Roo.each(b, function(tag) {
27484             if (w.indexOf(tag) > -1) {
27485                 return;
27486             }
27487             if (this.black.indexOf(tag) > -1) {
27488                 return;
27489             }
27490             this.black.push(tag);
27491             
27492         }, this);
27493         
27494         
27495         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27496         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27497         
27498         this.cwhite = [];
27499         this.cblack = [];
27500         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27501             if (b.indexOf(tag) > -1) {
27502                 return;
27503             }
27504             this.cwhite.push(tag);
27505             
27506         }, this);
27507         
27508         Roo.each(w, function(tag) {
27509             if (b.indexOf(tag) > -1) {
27510                 return;
27511             }
27512             if (this.cwhite.indexOf(tag) > -1) {
27513                 return;
27514             }
27515             this.cwhite.push(tag);
27516             
27517         }, this);
27518         
27519         
27520         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27521             if (w.indexOf(tag) > -1) {
27522                 return;
27523             }
27524             this.cblack.push(tag);
27525             
27526         }, this);
27527         
27528         Roo.each(b, function(tag) {
27529             if (w.indexOf(tag) > -1) {
27530                 return;
27531             }
27532             if (this.cblack.indexOf(tag) > -1) {
27533                 return;
27534             }
27535             this.cblack.push(tag);
27536             
27537         }, this);
27538     },
27539     
27540     setStylesheets : function(stylesheets)
27541     {
27542         if(typeof(stylesheets) == 'string'){
27543             Roo.get(this.iframe.contentDocument.head).createChild({
27544                 tag : 'link',
27545                 rel : 'stylesheet',
27546                 type : 'text/css',
27547                 href : stylesheets
27548             });
27549             
27550             return;
27551         }
27552         var _this = this;
27553      
27554         Roo.each(stylesheets, function(s) {
27555             if(!s.length){
27556                 return;
27557             }
27558             
27559             Roo.get(_this.iframe.contentDocument.head).createChild({
27560                 tag : 'link',
27561                 rel : 'stylesheet',
27562                 type : 'text/css',
27563                 href : s
27564             });
27565         });
27566
27567         
27568     },
27569     
27570     removeStylesheets : function()
27571     {
27572         var _this = this;
27573         
27574         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27575             s.remove();
27576         });
27577     },
27578     
27579     setStyle : function(style)
27580     {
27581         Roo.get(this.iframe.contentDocument.head).createChild({
27582             tag : 'style',
27583             type : 'text/css',
27584             html : style
27585         });
27586
27587         return;
27588     }
27589     
27590     // hide stuff that is not compatible
27591     /**
27592      * @event blur
27593      * @hide
27594      */
27595     /**
27596      * @event change
27597      * @hide
27598      */
27599     /**
27600      * @event focus
27601      * @hide
27602      */
27603     /**
27604      * @event specialkey
27605      * @hide
27606      */
27607     /**
27608      * @cfg {String} fieldClass @hide
27609      */
27610     /**
27611      * @cfg {String} focusClass @hide
27612      */
27613     /**
27614      * @cfg {String} autoCreate @hide
27615      */
27616     /**
27617      * @cfg {String} inputType @hide
27618      */
27619     /**
27620      * @cfg {String} invalidClass @hide
27621      */
27622     /**
27623      * @cfg {String} invalidText @hide
27624      */
27625     /**
27626      * @cfg {String} msgFx @hide
27627      */
27628     /**
27629      * @cfg {String} validateOnBlur @hide
27630      */
27631 });
27632
27633 Roo.HtmlEditorCore.white = [
27634         'area', 'br', 'img', 'input', 'hr', 'wbr',
27635         
27636        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27637        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27638        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27639        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27640        'table',   'ul',         'xmp', 
27641        
27642        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27643       'thead',   'tr', 
27644      
27645       'dir', 'menu', 'ol', 'ul', 'dl',
27646        
27647       'embed',  'object'
27648 ];
27649
27650
27651 Roo.HtmlEditorCore.black = [
27652     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27653         'applet', // 
27654         'base',   'basefont', 'bgsound', 'blink',  'body', 
27655         'frame',  'frameset', 'head',    'html',   'ilayer', 
27656         'iframe', 'layer',  'link',     'meta',    'object',   
27657         'script', 'style' ,'title',  'xml' // clean later..
27658 ];
27659 Roo.HtmlEditorCore.clean = [
27660     'script', 'style', 'title', 'xml'
27661 ];
27662 Roo.HtmlEditorCore.remove = [
27663     'font'
27664 ];
27665 // attributes..
27666
27667 Roo.HtmlEditorCore.ablack = [
27668     'on'
27669 ];
27670     
27671 Roo.HtmlEditorCore.aclean = [ 
27672     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27673 ];
27674
27675 // protocols..
27676 Roo.HtmlEditorCore.pwhite= [
27677         'http',  'https',  'mailto'
27678 ];
27679
27680 // white listed style attributes.
27681 Roo.HtmlEditorCore.cwhite= [
27682       //  'text-align', /// default is to allow most things..
27683       
27684          
27685 //        'font-size'//??
27686 ];
27687
27688 // black listed style attributes.
27689 Roo.HtmlEditorCore.cblack= [
27690       //  'font-size' -- this can be set by the project 
27691 ];
27692
27693
27694 Roo.HtmlEditorCore.swapCodes   =[ 
27695     [    8211, "&#8211;" ], 
27696     [    8212, "&#8212;" ], 
27697     [    8216,  "'" ],  
27698     [    8217, "'" ],  
27699     [    8220, '"' ],  
27700     [    8221, '"' ],  
27701     [    8226, "*" ],  
27702     [    8230, "..." ]
27703 ]; 
27704
27705     /*
27706  * - LGPL
27707  *
27708  * HtmlEditor
27709  * 
27710  */
27711
27712 /**
27713  * @class Roo.bootstrap.form.HtmlEditor
27714  * @extends Roo.bootstrap.form.TextArea
27715  * Bootstrap HtmlEditor class
27716
27717  * @constructor
27718  * Create a new HtmlEditor
27719  * @param {Object} config The config object
27720  */
27721
27722 Roo.bootstrap.form.HtmlEditor = function(config){
27723     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27724     if (!this.toolbars) {
27725         this.toolbars = [];
27726     }
27727     
27728     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27729     this.addEvents({
27730             /**
27731              * @event initialize
27732              * Fires when the editor is fully initialized (including the iframe)
27733              * @param {HtmlEditor} this
27734              */
27735             initialize: true,
27736             /**
27737              * @event activate
27738              * Fires when the editor is first receives the focus. Any insertion must wait
27739              * until after this event.
27740              * @param {HtmlEditor} this
27741              */
27742             activate: true,
27743              /**
27744              * @event beforesync
27745              * Fires before the textarea is updated with content from the editor iframe. Return false
27746              * to cancel the sync.
27747              * @param {HtmlEditor} this
27748              * @param {String} html
27749              */
27750             beforesync: true,
27751              /**
27752              * @event beforepush
27753              * Fires before the iframe editor is updated with content from the textarea. Return false
27754              * to cancel the push.
27755              * @param {HtmlEditor} this
27756              * @param {String} html
27757              */
27758             beforepush: true,
27759              /**
27760              * @event sync
27761              * Fires when the textarea is updated with content from the editor iframe.
27762              * @param {HtmlEditor} this
27763              * @param {String} html
27764              */
27765             sync: true,
27766              /**
27767              * @event push
27768              * Fires when the iframe editor is updated with content from the textarea.
27769              * @param {HtmlEditor} this
27770              * @param {String} html
27771              */
27772             push: true,
27773              /**
27774              * @event editmodechange
27775              * Fires when the editor switches edit modes
27776              * @param {HtmlEditor} this
27777              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27778              */
27779             editmodechange: true,
27780             /**
27781              * @event editorevent
27782              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27783              * @param {HtmlEditor} this
27784              */
27785             editorevent: true,
27786             /**
27787              * @event firstfocus
27788              * Fires when on first focus - needed by toolbars..
27789              * @param {HtmlEditor} this
27790              */
27791             firstfocus: true,
27792             /**
27793              * @event autosave
27794              * Auto save the htmlEditor value as a file into Events
27795              * @param {HtmlEditor} this
27796              */
27797             autosave: true,
27798             /**
27799              * @event savedpreview
27800              * preview the saved version of htmlEditor
27801              * @param {HtmlEditor} this
27802              */
27803             savedpreview: true
27804         });
27805 };
27806
27807
27808 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
27809     
27810     
27811       /**
27812      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27813      */
27814     toolbars : false,
27815     
27816      /**
27817     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27818     */
27819     btns : [],
27820    
27821      /**
27822      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27823      *                        Roo.resizable.
27824      */
27825     resizable : false,
27826      /**
27827      * @cfg {Number} height (in pixels)
27828      */   
27829     height: 300,
27830    /**
27831      * @cfg {Number} width (in pixels)
27832      */   
27833     width: false,
27834     
27835     /**
27836      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27837      * 
27838      */
27839     stylesheets: false,
27840     
27841     // id of frame..
27842     frameId: false,
27843     
27844     // private properties
27845     validationEvent : false,
27846     deferHeight: true,
27847     initialized : false,
27848     activated : false,
27849     
27850     onFocus : Roo.emptyFn,
27851     iframePad:3,
27852     hideMode:'offsets',
27853     
27854     tbContainer : false,
27855     
27856     bodyCls : '',
27857     
27858     toolbarContainer :function() {
27859         return this.wrap.select('.x-html-editor-tb',true).first();
27860     },
27861
27862     /**
27863      * Protected method that will not generally be called directly. It
27864      * is called when the editor creates its toolbar. Override this method if you need to
27865      * add custom toolbar buttons.
27866      * @param {HtmlEditor} editor
27867      */
27868     createToolbar : function(){
27869         Roo.log('renewing');
27870         Roo.log("create toolbars");
27871         
27872         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27873         this.toolbars[0].render(this.toolbarContainer());
27874         
27875         return;
27876         
27877 //        if (!editor.toolbars || !editor.toolbars.length) {
27878 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27879 //        }
27880 //        
27881 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27882 //            editor.toolbars[i] = Roo.factory(
27883 //                    typeof(editor.toolbars[i]) == 'string' ?
27884 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27885 //                Roo.bootstrap.form.HtmlEditor);
27886 //            editor.toolbars[i].init(editor);
27887 //        }
27888     },
27889
27890      
27891     // private
27892     onRender : function(ct, position)
27893     {
27894        // Roo.log("Call onRender: " + this.xtype);
27895         var _t = this;
27896         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27897       
27898         this.wrap = this.inputEl().wrap({
27899             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27900         });
27901         
27902         this.editorcore.onRender(ct, position);
27903          
27904         if (this.resizable) {
27905             this.resizeEl = new Roo.Resizable(this.wrap, {
27906                 pinned : true,
27907                 wrap: true,
27908                 dynamic : true,
27909                 minHeight : this.height,
27910                 height: this.height,
27911                 handles : this.resizable,
27912                 width: this.width,
27913                 listeners : {
27914                     resize : function(r, w, h) {
27915                         _t.onResize(w,h); // -something
27916                     }
27917                 }
27918             });
27919             
27920         }
27921         this.createToolbar(this);
27922        
27923         
27924         if(!this.width && this.resizable){
27925             this.setSize(this.wrap.getSize());
27926         }
27927         if (this.resizeEl) {
27928             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27929             // should trigger onReize..
27930         }
27931         
27932     },
27933
27934     // private
27935     onResize : function(w, h)
27936     {
27937         Roo.log('resize: ' +w + ',' + h );
27938         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27939         var ew = false;
27940         var eh = false;
27941         
27942         if(this.inputEl() ){
27943             if(typeof w == 'number'){
27944                 var aw = w - this.wrap.getFrameWidth('lr');
27945                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27946                 ew = aw;
27947             }
27948             if(typeof h == 'number'){
27949                  var tbh = -11;  // fixme it needs to tool bar size!
27950                 for (var i =0; i < this.toolbars.length;i++) {
27951                     // fixme - ask toolbars for heights?
27952                     tbh += this.toolbars[i].el.getHeight();
27953                     //if (this.toolbars[i].footer) {
27954                     //    tbh += this.toolbars[i].footer.el.getHeight();
27955                     //}
27956                 }
27957               
27958                 
27959                 
27960                 
27961                 
27962                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27963                 ah -= 5; // knock a few pixes off for look..
27964                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27965                 var eh = ah;
27966             }
27967         }
27968         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27969         this.editorcore.onResize(ew,eh);
27970         
27971     },
27972
27973     /**
27974      * Toggles the editor between standard and source edit mode.
27975      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27976      */
27977     toggleSourceEdit : function(sourceEditMode)
27978     {
27979         this.editorcore.toggleSourceEdit(sourceEditMode);
27980         
27981         if(this.editorcore.sourceEditMode){
27982             Roo.log('editor - showing textarea');
27983             
27984 //            Roo.log('in');
27985 //            Roo.log(this.syncValue());
27986             this.syncValue();
27987             this.inputEl().removeClass(['hide', 'x-hidden']);
27988             this.inputEl().dom.removeAttribute('tabIndex');
27989             this.inputEl().focus();
27990         }else{
27991             Roo.log('editor - hiding textarea');
27992 //            Roo.log('out')
27993 //            Roo.log(this.pushValue()); 
27994             this.pushValue();
27995             
27996             this.inputEl().addClass(['hide', 'x-hidden']);
27997             this.inputEl().dom.setAttribute('tabIndex', -1);
27998             //this.deferFocus();
27999         }
28000          
28001         if(this.resizable){
28002             this.setSize(this.wrap.getSize());
28003         }
28004         
28005         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
28006     },
28007  
28008     // private (for BoxComponent)
28009     adjustSize : Roo.BoxComponent.prototype.adjustSize,
28010
28011     // private (for BoxComponent)
28012     getResizeEl : function(){
28013         return this.wrap;
28014     },
28015
28016     // private (for BoxComponent)
28017     getPositionEl : function(){
28018         return this.wrap;
28019     },
28020
28021     // private
28022     initEvents : function(){
28023         this.originalValue = this.getValue();
28024     },
28025
28026 //    /**
28027 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28028 //     * @method
28029 //     */
28030 //    markInvalid : Roo.emptyFn,
28031 //    /**
28032 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28033 //     * @method
28034 //     */
28035 //    clearInvalid : Roo.emptyFn,
28036
28037     setValue : function(v){
28038         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28039         this.editorcore.pushValue();
28040     },
28041
28042      
28043     // private
28044     deferFocus : function(){
28045         this.focus.defer(10, this);
28046     },
28047
28048     // doc'ed in Field
28049     focus : function(){
28050         this.editorcore.focus();
28051         
28052     },
28053       
28054
28055     // private
28056     onDestroy : function(){
28057         
28058         
28059         
28060         if(this.rendered){
28061             
28062             for (var i =0; i < this.toolbars.length;i++) {
28063                 // fixme - ask toolbars for heights?
28064                 this.toolbars[i].onDestroy();
28065             }
28066             
28067             this.wrap.dom.innerHTML = '';
28068             this.wrap.remove();
28069         }
28070     },
28071
28072     // private
28073     onFirstFocus : function(){
28074         //Roo.log("onFirstFocus");
28075         this.editorcore.onFirstFocus();
28076          for (var i =0; i < this.toolbars.length;i++) {
28077             this.toolbars[i].onFirstFocus();
28078         }
28079         
28080     },
28081     
28082     // private
28083     syncValue : function()
28084     {   
28085         this.editorcore.syncValue();
28086     },
28087     
28088     pushValue : function()
28089     {   
28090         this.editorcore.pushValue();
28091     }
28092      
28093     
28094     // hide stuff that is not compatible
28095     /**
28096      * @event blur
28097      * @hide
28098      */
28099     /**
28100      * @event change
28101      * @hide
28102      */
28103     /**
28104      * @event focus
28105      * @hide
28106      */
28107     /**
28108      * @event specialkey
28109      * @hide
28110      */
28111     /**
28112      * @cfg {String} fieldClass @hide
28113      */
28114     /**
28115      * @cfg {String} focusClass @hide
28116      */
28117     /**
28118      * @cfg {String} autoCreate @hide
28119      */
28120     /**
28121      * @cfg {String} inputType @hide
28122      */
28123      
28124     /**
28125      * @cfg {String} invalidText @hide
28126      */
28127     /**
28128      * @cfg {String} msgFx @hide
28129      */
28130     /**
28131      * @cfg {String} validateOnBlur @hide
28132      */
28133 });
28134  
28135     
28136    
28137    
28138    
28139       
28140 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28141 /**
28142  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28143  * @parent Roo.bootstrap.form.HtmlEditor
28144  * @extends Roo.bootstrap.nav.Simplebar
28145  * Basic Toolbar
28146  * 
28147  * @example
28148  * Usage:
28149  *
28150  new Roo.bootstrap.form.HtmlEditor({
28151     ....
28152     toolbars : [
28153         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28154             disable : { fonts: 1 , format: 1, ..., ... , ...],
28155             btns : [ .... ]
28156         })
28157     }
28158      
28159  * 
28160  * @cfg {Object} disable List of elements to disable..
28161  * @cfg {Array} btns List of additional buttons.
28162  * 
28163  * 
28164  * NEEDS Extra CSS? 
28165  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28166  */
28167  
28168 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28169 {
28170     
28171     Roo.apply(this, config);
28172     
28173     // default disabled, based on 'good practice'..
28174     this.disable = this.disable || {};
28175     Roo.applyIf(this.disable, {
28176         fontSize : true,
28177         colors : true,
28178         specialElements : true
28179     });
28180     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28181     
28182     this.editor = config.editor;
28183     this.editorcore = config.editor.editorcore;
28184     
28185     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28186     
28187     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28188     // dont call parent... till later.
28189 }
28190 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
28191      
28192     bar : true,
28193     
28194     editor : false,
28195     editorcore : false,
28196     
28197     
28198     formats : [
28199         "p" ,  
28200         "h1","h2","h3","h4","h5","h6", 
28201         "pre", "code", 
28202         "abbr", "acronym", "address", "cite", "samp", "var",
28203         'div','span'
28204     ],
28205     
28206     onRender : function(ct, position)
28207     {
28208        // Roo.log("Call onRender: " + this.xtype);
28209         
28210        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28211        Roo.log(this.el);
28212        this.el.dom.style.marginBottom = '0';
28213        var _this = this;
28214        var editorcore = this.editorcore;
28215        var editor= this.editor;
28216        
28217        var children = [];
28218        var btn = function(id,cmd , toggle, handler, html){
28219        
28220             var  event = toggle ? 'toggle' : 'click';
28221        
28222             var a = {
28223                 size : 'sm',
28224                 xtype: 'Button',
28225                 xns: Roo.bootstrap,
28226                 //glyphicon : id,
28227                 fa: id,
28228                 cmd : id || cmd,
28229                 enableToggle:toggle !== false,
28230                 html : html || '',
28231                 pressed : toggle ? false : null,
28232                 listeners : {}
28233             };
28234             a.listeners[toggle ? 'toggle' : 'click'] = function() {
28235                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
28236             };
28237             children.push(a);
28238             return a;
28239        }
28240        
28241     //    var cb_box = function...
28242         
28243         var style = {
28244                 xtype: 'Button',
28245                 size : 'sm',
28246                 xns: Roo.bootstrap,
28247                 fa : 'font',
28248                 //html : 'submit'
28249                 menu : {
28250                     xtype: 'Menu',
28251                     xns: Roo.bootstrap,
28252                     items:  []
28253                 }
28254         };
28255         Roo.each(this.formats, function(f) {
28256             style.menu.items.push({
28257                 xtype :'MenuItem',
28258                 xns: Roo.bootstrap,
28259                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28260                 tagname : f,
28261                 listeners : {
28262                     click : function()
28263                     {
28264                         editorcore.insertTag(this.tagname);
28265                         editor.focus();
28266                     }
28267                 }
28268                 
28269             });
28270         });
28271         children.push(style);   
28272         
28273         btn('bold',false,true);
28274         btn('italic',false,true);
28275         btn('align-left', 'justifyleft',true);
28276         btn('align-center', 'justifycenter',true);
28277         btn('align-right' , 'justifyright',true);
28278         btn('link', false, false, function(btn) {
28279             //Roo.log("create link?");
28280             var url = prompt(this.createLinkText, this.defaultLinkValue);
28281             if(url && url != 'http:/'+'/'){
28282                 this.editorcore.relayCmd('createlink', url);
28283             }
28284         }),
28285         btn('list','insertunorderedlist',true);
28286         btn('pencil', false,true, function(btn){
28287                 Roo.log(this);
28288                 this.toggleSourceEdit(btn.pressed);
28289         });
28290         
28291         if (this.editor.btns.length > 0) {
28292             for (var i = 0; i<this.editor.btns.length; i++) {
28293                 children.push(this.editor.btns[i]);
28294             }
28295         }
28296         
28297         /*
28298         var cog = {
28299                 xtype: 'Button',
28300                 size : 'sm',
28301                 xns: Roo.bootstrap,
28302                 glyphicon : 'cog',
28303                 //html : 'submit'
28304                 menu : {
28305                     xtype: 'Menu',
28306                     xns: Roo.bootstrap,
28307                     items:  []
28308                 }
28309         };
28310         
28311         cog.menu.items.push({
28312             xtype :'MenuItem',
28313             xns: Roo.bootstrap,
28314             html : Clean styles,
28315             tagname : f,
28316             listeners : {
28317                 click : function()
28318                 {
28319                     editorcore.insertTag(this.tagname);
28320                     editor.focus();
28321                 }
28322             }
28323             
28324         });
28325        */
28326         
28327          
28328        this.xtype = 'NavSimplebar';
28329         
28330         for(var i=0;i< children.length;i++) {
28331             
28332             this.buttons.add(this.addxtypeChild(children[i]));
28333             
28334         }
28335         
28336         editor.on('editorevent', this.updateToolbar, this);
28337     },
28338     onBtnClick : function(id)
28339     {
28340        this.editorcore.relayCmd(id);
28341        this.editorcore.focus();
28342     },
28343     
28344     /**
28345      * Protected method that will not generally be called directly. It triggers
28346      * a toolbar update by reading the markup state of the current selection in the editor.
28347      */
28348     updateToolbar: function(){
28349
28350         if(!this.editorcore.activated){
28351             this.editor.onFirstFocus(); // is this neeed?
28352             return;
28353         }
28354
28355         var btns = this.buttons; 
28356         var doc = this.editorcore.doc;
28357         btns.get('bold').setActive(doc.queryCommandState('bold'));
28358         btns.get('italic').setActive(doc.queryCommandState('italic'));
28359         //btns.get('underline').setActive(doc.queryCommandState('underline'));
28360         
28361         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28362         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28363         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28364         
28365         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28366         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28367          /*
28368         
28369         var ans = this.editorcore.getAllAncestors();
28370         if (this.formatCombo) {
28371             
28372             
28373             var store = this.formatCombo.store;
28374             this.formatCombo.setValue("");
28375             for (var i =0; i < ans.length;i++) {
28376                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28377                     // select it..
28378                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28379                     break;
28380                 }
28381             }
28382         }
28383         
28384         
28385         
28386         // hides menus... - so this cant be on a menu...
28387         Roo.bootstrap.MenuMgr.hideAll();
28388         */
28389         Roo.bootstrap.menu.Manager.hideAll();
28390         //this.editorsyncValue();
28391     },
28392     onFirstFocus: function() {
28393         this.buttons.each(function(item){
28394            item.enable();
28395         });
28396     },
28397     toggleSourceEdit : function(sourceEditMode){
28398         
28399           
28400         if(sourceEditMode){
28401             Roo.log("disabling buttons");
28402            this.buttons.each( function(item){
28403                 if(item.cmd != 'pencil'){
28404                     item.disable();
28405                 }
28406             });
28407           
28408         }else{
28409             Roo.log("enabling buttons");
28410             if(this.editorcore.initialized){
28411                 this.buttons.each( function(item){
28412                     item.enable();
28413                 });
28414             }
28415             
28416         }
28417         Roo.log("calling toggole on editor");
28418         // tell the editor that it's been pressed..
28419         this.editor.toggleSourceEdit(sourceEditMode);
28420        
28421     }
28422 });
28423
28424
28425
28426
28427  
28428 /*
28429  * - LGPL
28430  */
28431
28432 /**
28433  * @class Roo.bootstrap.form.Markdown
28434  * @extends Roo.bootstrap.form.TextArea
28435  * Bootstrap Showdown editable area
28436  * @cfg {string} content
28437  * 
28438  * @constructor
28439  * Create a new Showdown
28440  */
28441
28442 Roo.bootstrap.form.Markdown = function(config){
28443     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28444    
28445 };
28446
28447 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
28448     
28449     editing :false,
28450     
28451     initEvents : function()
28452     {
28453         
28454         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28455         this.markdownEl = this.el.createChild({
28456             cls : 'roo-markdown-area'
28457         });
28458         this.inputEl().addClass('d-none');
28459         if (this.getValue() == '') {
28460             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28461             
28462         } else {
28463             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28464         }
28465         this.markdownEl.on('click', this.toggleTextEdit, this);
28466         this.on('blur', this.toggleTextEdit, this);
28467         this.on('specialkey', this.resizeTextArea, this);
28468     },
28469     
28470     toggleTextEdit : function()
28471     {
28472         var sh = this.markdownEl.getHeight();
28473         this.inputEl().addClass('d-none');
28474         this.markdownEl.addClass('d-none');
28475         if (!this.editing) {
28476             // show editor?
28477             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28478             this.inputEl().removeClass('d-none');
28479             this.inputEl().focus();
28480             this.editing = true;
28481             return;
28482         }
28483         // show showdown...
28484         this.updateMarkdown();
28485         this.markdownEl.removeClass('d-none');
28486         this.editing = false;
28487         return;
28488     },
28489     updateMarkdown : function()
28490     {
28491         if (this.getValue() == '') {
28492             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28493             return;
28494         }
28495  
28496         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28497     },
28498     
28499     resizeTextArea: function () {
28500         
28501         var sh = 100;
28502         Roo.log([sh, this.getValue().split("\n").length * 30]);
28503         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28504     },
28505     setValue : function(val)
28506     {
28507         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28508         if (!this.editing) {
28509             this.updateMarkdown();
28510         }
28511         
28512     },
28513     focus : function()
28514     {
28515         if (!this.editing) {
28516             this.toggleTextEdit();
28517         }
28518         
28519     }
28520
28521
28522 });/*
28523  * Based on:
28524  * Ext JS Library 1.1.1
28525  * Copyright(c) 2006-2007, Ext JS, LLC.
28526  *
28527  * Originally Released Under LGPL - original licence link has changed is not relivant.
28528  *
28529  * Fork - LGPL
28530  * <script type="text/javascript">
28531  */
28532  
28533 /**
28534  * @class Roo.bootstrap.PagingToolbar
28535  * @extends Roo.bootstrap.nav.Simplebar
28536  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28537  * @constructor
28538  * Create a new PagingToolbar
28539  * @param {Object} config The config object
28540  * @param {Roo.data.Store} store
28541  */
28542 Roo.bootstrap.PagingToolbar = function(config)
28543 {
28544     // old args format still supported... - xtype is prefered..
28545         // created from xtype...
28546     
28547     this.ds = config.dataSource;
28548     
28549     if (config.store && !this.ds) {
28550         this.store= Roo.factory(config.store, Roo.data);
28551         this.ds = this.store;
28552         this.ds.xmodule = this.xmodule || false;
28553     }
28554     
28555     this.toolbarItems = [];
28556     if (config.items) {
28557         this.toolbarItems = config.items;
28558     }
28559     
28560     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28561     
28562     this.cursor = 0;
28563     
28564     if (this.ds) { 
28565         this.bind(this.ds);
28566     }
28567     
28568     if (Roo.bootstrap.version == 4) {
28569         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28570     } else {
28571         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28572     }
28573     
28574 };
28575
28576 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28577     /**
28578      * @cfg {Roo.bootstrap.Button} buttons[]
28579      * Buttons for the toolbar
28580      */
28581      /**
28582      * @cfg {Roo.data.Store} store
28583      * The underlying data store providing the paged data
28584      */
28585     /**
28586      * @cfg {String/HTMLElement/Element} container
28587      * container The id or element that will contain the toolbar
28588      */
28589     /**
28590      * @cfg {Boolean} displayInfo
28591      * True to display the displayMsg (defaults to false)
28592      */
28593     /**
28594      * @cfg {Number} pageSize
28595      * The number of records to display per page (defaults to 20)
28596      */
28597     pageSize: 20,
28598     /**
28599      * @cfg {String} displayMsg
28600      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28601      */
28602     displayMsg : 'Displaying {0} - {1} of {2}',
28603     /**
28604      * @cfg {String} emptyMsg
28605      * The message to display when no records are found (defaults to "No data to display")
28606      */
28607     emptyMsg : 'No data to display',
28608     /**
28609      * Customizable piece of the default paging text (defaults to "Page")
28610      * @type String
28611      */
28612     beforePageText : "Page",
28613     /**
28614      * Customizable piece of the default paging text (defaults to "of %0")
28615      * @type String
28616      */
28617     afterPageText : "of {0}",
28618     /**
28619      * Customizable piece of the default paging text (defaults to "First Page")
28620      * @type String
28621      */
28622     firstText : "First Page",
28623     /**
28624      * Customizable piece of the default paging text (defaults to "Previous Page")
28625      * @type String
28626      */
28627     prevText : "Previous Page",
28628     /**
28629      * Customizable piece of the default paging text (defaults to "Next Page")
28630      * @type String
28631      */
28632     nextText : "Next Page",
28633     /**
28634      * Customizable piece of the default paging text (defaults to "Last Page")
28635      * @type String
28636      */
28637     lastText : "Last Page",
28638     /**
28639      * Customizable piece of the default paging text (defaults to "Refresh")
28640      * @type String
28641      */
28642     refreshText : "Refresh",
28643
28644     buttons : false,
28645     // private
28646     onRender : function(ct, position) 
28647     {
28648         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28649         this.navgroup.parentId = this.id;
28650         this.navgroup.onRender(this.el, null);
28651         // add the buttons to the navgroup
28652         
28653         if(this.displayInfo){
28654             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28655             this.displayEl = this.el.select('.x-paging-info', true).first();
28656 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28657 //            this.displayEl = navel.el.select('span',true).first();
28658         }
28659         
28660         var _this = this;
28661         
28662         if(this.buttons){
28663             Roo.each(_this.buttons, function(e){ // this might need to use render????
28664                Roo.factory(e).render(_this.el);
28665             });
28666         }
28667             
28668         Roo.each(_this.toolbarItems, function(e) {
28669             _this.navgroup.addItem(e);
28670         });
28671         
28672         
28673         this.first = this.navgroup.addItem({
28674             tooltip: this.firstText,
28675             cls: "prev btn-outline-secondary",
28676             html : ' <i class="fa fa-step-backward"></i>',
28677             disabled: true,
28678             preventDefault: true,
28679             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28680         });
28681         
28682         this.prev =  this.navgroup.addItem({
28683             tooltip: this.prevText,
28684             cls: "prev btn-outline-secondary",
28685             html : ' <i class="fa fa-backward"></i>',
28686             disabled: true,
28687             preventDefault: true,
28688             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28689         });
28690     //this.addSeparator();
28691         
28692         
28693         var field = this.navgroup.addItem( {
28694             tagtype : 'span',
28695             cls : 'x-paging-position  btn-outline-secondary',
28696              disabled: true,
28697             html : this.beforePageText  +
28698                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28699                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28700          } ); //?? escaped?
28701         
28702         this.field = field.el.select('input', true).first();
28703         this.field.on("keydown", this.onPagingKeydown, this);
28704         this.field.on("focus", function(){this.dom.select();});
28705     
28706     
28707         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28708         //this.field.setHeight(18);
28709         //this.addSeparator();
28710         this.next = this.navgroup.addItem({
28711             tooltip: this.nextText,
28712             cls: "next btn-outline-secondary",
28713             html : ' <i class="fa fa-forward"></i>',
28714             disabled: true,
28715             preventDefault: true,
28716             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28717         });
28718         this.last = this.navgroup.addItem({
28719             tooltip: this.lastText,
28720             html : ' <i class="fa fa-step-forward"></i>',
28721             cls: "next btn-outline-secondary",
28722             disabled: true,
28723             preventDefault: true,
28724             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28725         });
28726     //this.addSeparator();
28727         this.loading = this.navgroup.addItem({
28728             tooltip: this.refreshText,
28729             cls: "btn-outline-secondary",
28730             html : ' <i class="fa fa-refresh"></i>',
28731             preventDefault: true,
28732             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28733         });
28734         
28735     },
28736
28737     // private
28738     updateInfo : function(){
28739         if(this.displayEl){
28740             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28741             var msg = count == 0 ?
28742                 this.emptyMsg :
28743                 String.format(
28744                     this.displayMsg,
28745                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28746                 );
28747             this.displayEl.update(msg);
28748         }
28749     },
28750
28751     // private
28752     onLoad : function(ds, r, o)
28753     {
28754         this.cursor = o.params && o.params.start ? o.params.start : 0;
28755         
28756         var d = this.getPageData(),
28757             ap = d.activePage,
28758             ps = d.pages;
28759         
28760         
28761         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28762         this.field.dom.value = ap;
28763         this.first.setDisabled(ap == 1);
28764         this.prev.setDisabled(ap == 1);
28765         this.next.setDisabled(ap == ps);
28766         this.last.setDisabled(ap == ps);
28767         this.loading.enable();
28768         this.updateInfo();
28769     },
28770
28771     // private
28772     getPageData : function(){
28773         var total = this.ds.getTotalCount();
28774         return {
28775             total : total,
28776             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28777             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28778         };
28779     },
28780
28781     // private
28782     onLoadError : function(proxy, o){
28783         this.loading.enable();
28784         if (this.ds.events.loadexception.listeners.length  < 2) {
28785             // nothing has been assigned to loadexception except this...
28786             // so 
28787             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
28788
28789         }
28790     },
28791
28792     // private
28793     onPagingKeydown : function(e){
28794         var k = e.getKey();
28795         var d = this.getPageData();
28796         if(k == e.RETURN){
28797             var v = this.field.dom.value, pageNum;
28798             if(!v || isNaN(pageNum = parseInt(v, 10))){
28799                 this.field.dom.value = d.activePage;
28800                 return;
28801             }
28802             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28803             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28804             e.stopEvent();
28805         }
28806         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))
28807         {
28808           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28809           this.field.dom.value = pageNum;
28810           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28811           e.stopEvent();
28812         }
28813         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28814         {
28815           var v = this.field.dom.value, pageNum; 
28816           var increment = (e.shiftKey) ? 10 : 1;
28817           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28818                 increment *= -1;
28819           }
28820           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28821             this.field.dom.value = d.activePage;
28822             return;
28823           }
28824           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28825           {
28826             this.field.dom.value = parseInt(v, 10) + increment;
28827             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28828             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28829           }
28830           e.stopEvent();
28831         }
28832     },
28833
28834     // private
28835     beforeLoad : function(){
28836         if(this.loading){
28837             this.loading.disable();
28838         }
28839     },
28840
28841     // private
28842     onClick : function(which){
28843         
28844         var ds = this.ds;
28845         if (!ds) {
28846             return;
28847         }
28848         
28849         switch(which){
28850             case "first":
28851                 ds.load({params:{start: 0, limit: this.pageSize}});
28852             break;
28853             case "prev":
28854                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28855             break;
28856             case "next":
28857                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28858             break;
28859             case "last":
28860                 var total = ds.getTotalCount();
28861                 var extra = total % this.pageSize;
28862                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28863                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28864             break;
28865             case "refresh":
28866                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28867             break;
28868         }
28869     },
28870
28871     /**
28872      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28873      * @param {Roo.data.Store} store The data store to unbind
28874      */
28875     unbind : function(ds){
28876         ds.un("beforeload", this.beforeLoad, this);
28877         ds.un("load", this.onLoad, this);
28878         ds.un("loadexception", this.onLoadError, this);
28879         ds.un("remove", this.updateInfo, this);
28880         ds.un("add", this.updateInfo, this);
28881         this.ds = undefined;
28882     },
28883
28884     /**
28885      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28886      * @param {Roo.data.Store} store The data store to bind
28887      */
28888     bind : function(ds){
28889         ds.on("beforeload", this.beforeLoad, this);
28890         ds.on("load", this.onLoad, this);
28891         ds.on("loadexception", this.onLoadError, this);
28892         ds.on("remove", this.updateInfo, this);
28893         ds.on("add", this.updateInfo, this);
28894         this.ds = ds;
28895     }
28896 });/*
28897  * - LGPL
28898  *
28899  * element
28900  * 
28901  */
28902
28903 /**
28904  * @class Roo.bootstrap.MessageBar
28905  * @extends Roo.bootstrap.Component
28906  * Bootstrap MessageBar class
28907  * @cfg {String} html contents of the MessageBar
28908  * @cfg {String} weight (info | success | warning | danger) default info
28909  * @cfg {String} beforeClass insert the bar before the given class
28910  * @cfg {Boolean} closable (true | false) default false
28911  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28912  * 
28913  * @constructor
28914  * Create a new Element
28915  * @param {Object} config The config object
28916  */
28917
28918 Roo.bootstrap.MessageBar = function(config){
28919     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28920 };
28921
28922 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28923     
28924     html: '',
28925     weight: 'info',
28926     closable: false,
28927     fixed: false,
28928     beforeClass: 'bootstrap-sticky-wrap',
28929     
28930     getAutoCreate : function(){
28931         
28932         var cfg = {
28933             tag: 'div',
28934             cls: 'alert alert-dismissable alert-' + this.weight,
28935             cn: [
28936                 {
28937                     tag: 'span',
28938                     cls: 'message',
28939                     html: this.html || ''
28940                 }
28941             ]
28942         };
28943         
28944         if(this.fixed){
28945             cfg.cls += ' alert-messages-fixed';
28946         }
28947         
28948         if(this.closable){
28949             cfg.cn.push({
28950                 tag: 'button',
28951                 cls: 'close',
28952                 html: 'x'
28953             });
28954         }
28955         
28956         return cfg;
28957     },
28958     
28959     onRender : function(ct, position)
28960     {
28961         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28962         
28963         if(!this.el){
28964             var cfg = Roo.apply({},  this.getAutoCreate());
28965             cfg.id = Roo.id();
28966             
28967             if (this.cls) {
28968                 cfg.cls += ' ' + this.cls;
28969             }
28970             if (this.style) {
28971                 cfg.style = this.style;
28972             }
28973             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28974             
28975             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28976         }
28977         
28978         this.el.select('>button.close').on('click', this.hide, this);
28979         
28980     },
28981     
28982     show : function()
28983     {
28984         if (!this.rendered) {
28985             this.render();
28986         }
28987         
28988         this.el.show();
28989         
28990         this.fireEvent('show', this);
28991         
28992     },
28993     
28994     hide : function()
28995     {
28996         if (!this.rendered) {
28997             this.render();
28998         }
28999         
29000         this.el.hide();
29001         
29002         this.fireEvent('hide', this);
29003     },
29004     
29005     update : function()
29006     {
29007 //        var e = this.el.dom.firstChild;
29008 //        
29009 //        if(this.closable){
29010 //            e = e.nextSibling;
29011 //        }
29012 //        
29013 //        e.data = this.html || '';
29014
29015         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29016     }
29017    
29018 });
29019
29020  
29021
29022      /*
29023  * - LGPL
29024  *
29025  * Graph
29026  * 
29027  */
29028
29029
29030 /**
29031  * @class Roo.bootstrap.Graph
29032  * @extends Roo.bootstrap.Component
29033  * Bootstrap Graph class
29034 > Prameters
29035  -sm {number} sm 4
29036  -md {number} md 5
29037  @cfg {String} graphtype  bar | vbar | pie
29038  @cfg {number} g_x coodinator | centre x (pie)
29039  @cfg {number} g_y coodinator | centre y (pie)
29040  @cfg {number} g_r radius (pie)
29041  @cfg {number} g_height height of the chart (respected by all elements in the set)
29042  @cfg {number} g_width width of the chart (respected by all elements in the set)
29043  @cfg {Object} title The title of the chart
29044     
29045  -{Array}  values
29046  -opts (object) options for the chart 
29047      o {
29048      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29049      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29050      o vgutter (number)
29051      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.
29052      o stacked (boolean) whether or not to tread values as in a stacked bar chart
29053      o to
29054      o stretch (boolean)
29055      o }
29056  -opts (object) options for the pie
29057      o{
29058      o cut
29059      o startAngle (number)
29060      o endAngle (number)
29061      } 
29062  *
29063  * @constructor
29064  * Create a new Input
29065  * @param {Object} config The config object
29066  */
29067
29068 Roo.bootstrap.Graph = function(config){
29069     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29070     
29071     this.addEvents({
29072         // img events
29073         /**
29074          * @event click
29075          * The img click event for the img.
29076          * @param {Roo.EventObject} e
29077          */
29078         "click" : true
29079     });
29080 };
29081
29082 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
29083     
29084     sm: 4,
29085     md: 5,
29086     graphtype: 'bar',
29087     g_height: 250,
29088     g_width: 400,
29089     g_x: 50,
29090     g_y: 50,
29091     g_r: 30,
29092     opts:{
29093         //g_colors: this.colors,
29094         g_type: 'soft',
29095         g_gutter: '20%'
29096
29097     },
29098     title : false,
29099
29100     getAutoCreate : function(){
29101         
29102         var cfg = {
29103             tag: 'div',
29104             html : null
29105         };
29106         
29107         
29108         return  cfg;
29109     },
29110
29111     onRender : function(ct,position){
29112         
29113         
29114         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29115         
29116         if (typeof(Raphael) == 'undefined') {
29117             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29118             return;
29119         }
29120         
29121         this.raphael = Raphael(this.el.dom);
29122         
29123                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29124                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29125                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29126                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29127                 /*
29128                 r.text(160, 10, "Single Series Chart").attr(txtattr);
29129                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29130                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29131                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29132                 
29133                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29134                 r.barchart(330, 10, 300, 220, data1);
29135                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29136                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29137                 */
29138                 
29139                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29140                 // r.barchart(30, 30, 560, 250,  xdata, {
29141                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29142                 //     axis : "0 0 1 1",
29143                 //     axisxlabels :  xdata
29144                 //     //yvalues : cols,
29145                    
29146                 // });
29147 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29148 //        
29149 //        this.load(null,xdata,{
29150 //                axis : "0 0 1 1",
29151 //                axisxlabels :  xdata
29152 //                });
29153
29154     },
29155
29156     load : function(graphtype,xdata,opts)
29157     {
29158         this.raphael.clear();
29159         if(!graphtype) {
29160             graphtype = this.graphtype;
29161         }
29162         if(!opts){
29163             opts = this.opts;
29164         }
29165         var r = this.raphael,
29166             fin = function () {
29167                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29168             },
29169             fout = function () {
29170                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29171             },
29172             pfin = function() {
29173                 this.sector.stop();
29174                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29175
29176                 if (this.label) {
29177                     this.label[0].stop();
29178                     this.label[0].attr({ r: 7.5 });
29179                     this.label[1].attr({ "font-weight": 800 });
29180                 }
29181             },
29182             pfout = function() {
29183                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29184
29185                 if (this.label) {
29186                     this.label[0].animate({ r: 5 }, 500, "bounce");
29187                     this.label[1].attr({ "font-weight": 400 });
29188                 }
29189             };
29190
29191         switch(graphtype){
29192             case 'bar':
29193                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29194                 break;
29195             case 'hbar':
29196                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29197                 break;
29198             case 'pie':
29199 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
29200 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29201 //            
29202                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29203                 
29204                 break;
29205
29206         }
29207         
29208         if(this.title){
29209             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29210         }
29211         
29212     },
29213     
29214     setTitle: function(o)
29215     {
29216         this.title = o;
29217     },
29218     
29219     initEvents: function() {
29220         
29221         if(!this.href){
29222             this.el.on('click', this.onClick, this);
29223         }
29224     },
29225     
29226     onClick : function(e)
29227     {
29228         Roo.log('img onclick');
29229         this.fireEvent('click', this, e);
29230     }
29231    
29232 });
29233
29234  
29235 /*
29236  * - LGPL
29237  *
29238  * numberBox
29239  * 
29240  */
29241 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29242
29243 /**
29244  * @class Roo.bootstrap.dash.NumberBox
29245  * @extends Roo.bootstrap.Component
29246  * Bootstrap NumberBox class
29247  * @cfg {String} headline Box headline
29248  * @cfg {String} content Box content
29249  * @cfg {String} icon Box icon
29250  * @cfg {String} footer Footer text
29251  * @cfg {String} fhref Footer href
29252  * 
29253  * @constructor
29254  * Create a new NumberBox
29255  * @param {Object} config The config object
29256  */
29257
29258
29259 Roo.bootstrap.dash.NumberBox = function(config){
29260     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29261     
29262 };
29263
29264 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
29265     
29266     headline : '',
29267     content : '',
29268     icon : '',
29269     footer : '',
29270     fhref : '',
29271     ficon : '',
29272     
29273     getAutoCreate : function(){
29274         
29275         var cfg = {
29276             tag : 'div',
29277             cls : 'small-box ',
29278             cn : [
29279                 {
29280                     tag : 'div',
29281                     cls : 'inner',
29282                     cn :[
29283                         {
29284                             tag : 'h3',
29285                             cls : 'roo-headline',
29286                             html : this.headline
29287                         },
29288                         {
29289                             tag : 'p',
29290                             cls : 'roo-content',
29291                             html : this.content
29292                         }
29293                     ]
29294                 }
29295             ]
29296         };
29297         
29298         if(this.icon){
29299             cfg.cn.push({
29300                 tag : 'div',
29301                 cls : 'icon',
29302                 cn :[
29303                     {
29304                         tag : 'i',
29305                         cls : 'ion ' + this.icon
29306                     }
29307                 ]
29308             });
29309         }
29310         
29311         if(this.footer){
29312             var footer = {
29313                 tag : 'a',
29314                 cls : 'small-box-footer',
29315                 href : this.fhref || '#',
29316                 html : this.footer
29317             };
29318             
29319             cfg.cn.push(footer);
29320             
29321         }
29322         
29323         return  cfg;
29324     },
29325
29326     onRender : function(ct,position){
29327         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29328
29329
29330        
29331                 
29332     },
29333
29334     setHeadline: function (value)
29335     {
29336         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29337     },
29338     
29339     setFooter: function (value, href)
29340     {
29341         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29342         
29343         if(href){
29344             this.el.select('a.small-box-footer',true).first().attr('href', href);
29345         }
29346         
29347     },
29348
29349     setContent: function (value)
29350     {
29351         this.el.select('.roo-content',true).first().dom.innerHTML = value;
29352     },
29353
29354     initEvents: function() 
29355     {   
29356         
29357     }
29358     
29359 });
29360
29361  
29362 /*
29363  * - LGPL
29364  *
29365  * TabBox
29366  * 
29367  */
29368 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29369
29370 /**
29371  * @class Roo.bootstrap.dash.TabBox
29372  * @extends Roo.bootstrap.Component
29373  * @children Roo.bootstrap.dash.TabPane
29374  * Bootstrap TabBox class
29375  * @cfg {String} title Title of the TabBox
29376  * @cfg {String} icon Icon of the TabBox
29377  * @cfg {Boolean} showtabs (true|false) show the tabs default true
29378  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29379  * 
29380  * @constructor
29381  * Create a new TabBox
29382  * @param {Object} config The config object
29383  */
29384
29385
29386 Roo.bootstrap.dash.TabBox = function(config){
29387     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29388     this.addEvents({
29389         // raw events
29390         /**
29391          * @event addpane
29392          * When a pane is added
29393          * @param {Roo.bootstrap.dash.TabPane} pane
29394          */
29395         "addpane" : true,
29396         /**
29397          * @event activatepane
29398          * When a pane is activated
29399          * @param {Roo.bootstrap.dash.TabPane} pane
29400          */
29401         "activatepane" : true
29402         
29403          
29404     });
29405     
29406     this.panes = [];
29407 };
29408
29409 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
29410
29411     title : '',
29412     icon : false,
29413     showtabs : true,
29414     tabScrollable : false,
29415     
29416     getChildContainer : function()
29417     {
29418         return this.el.select('.tab-content', true).first();
29419     },
29420     
29421     getAutoCreate : function(){
29422         
29423         var header = {
29424             tag: 'li',
29425             cls: 'pull-left header',
29426             html: this.title,
29427             cn : []
29428         };
29429         
29430         if(this.icon){
29431             header.cn.push({
29432                 tag: 'i',
29433                 cls: 'fa ' + this.icon
29434             });
29435         }
29436         
29437         var h = {
29438             tag: 'ul',
29439             cls: 'nav nav-tabs pull-right',
29440             cn: [
29441                 header
29442             ]
29443         };
29444         
29445         if(this.tabScrollable){
29446             h = {
29447                 tag: 'div',
29448                 cls: 'tab-header',
29449                 cn: [
29450                     {
29451                         tag: 'ul',
29452                         cls: 'nav nav-tabs pull-right',
29453                         cn: [
29454                             header
29455                         ]
29456                     }
29457                 ]
29458             };
29459         }
29460         
29461         var cfg = {
29462             tag: 'div',
29463             cls: 'nav-tabs-custom',
29464             cn: [
29465                 h,
29466                 {
29467                     tag: 'div',
29468                     cls: 'tab-content no-padding',
29469                     cn: []
29470                 }
29471             ]
29472         };
29473
29474         return  cfg;
29475     },
29476     initEvents : function()
29477     {
29478         //Roo.log('add add pane handler');
29479         this.on('addpane', this.onAddPane, this);
29480     },
29481      /**
29482      * Updates the box title
29483      * @param {String} html to set the title to.
29484      */
29485     setTitle : function(value)
29486     {
29487         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29488     },
29489     onAddPane : function(pane)
29490     {
29491         this.panes.push(pane);
29492         //Roo.log('addpane');
29493         //Roo.log(pane);
29494         // tabs are rendere left to right..
29495         if(!this.showtabs){
29496             return;
29497         }
29498         
29499         var ctr = this.el.select('.nav-tabs', true).first();
29500          
29501          
29502         var existing = ctr.select('.nav-tab',true);
29503         var qty = existing.getCount();;
29504         
29505         
29506         var tab = ctr.createChild({
29507             tag : 'li',
29508             cls : 'nav-tab' + (qty ? '' : ' active'),
29509             cn : [
29510                 {
29511                     tag : 'a',
29512                     href:'#',
29513                     html : pane.title
29514                 }
29515             ]
29516         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29517         pane.tab = tab;
29518         
29519         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29520         if (!qty) {
29521             pane.el.addClass('active');
29522         }
29523         
29524                 
29525     },
29526     onTabClick : function(ev,un,ob,pane)
29527     {
29528         //Roo.log('tab - prev default');
29529         ev.preventDefault();
29530         
29531         
29532         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29533         pane.tab.addClass('active');
29534         //Roo.log(pane.title);
29535         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29536         // technically we should have a deactivate event.. but maybe add later.
29537         // and it should not de-activate the selected tab...
29538         this.fireEvent('activatepane', pane);
29539         pane.el.addClass('active');
29540         pane.fireEvent('activate');
29541         
29542         
29543     },
29544     
29545     getActivePane : function()
29546     {
29547         var r = false;
29548         Roo.each(this.panes, function(p) {
29549             if(p.el.hasClass('active')){
29550                 r = p;
29551                 return false;
29552             }
29553             
29554             return;
29555         });
29556         
29557         return r;
29558     }
29559     
29560     
29561 });
29562
29563  
29564 /*
29565  * - LGPL
29566  *
29567  * Tab pane
29568  * 
29569  */
29570 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29571 /**
29572  * @class Roo.bootstrap.TabPane
29573  * @extends Roo.bootstrap.Component
29574  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
29575  * Bootstrap TabPane class
29576  * @cfg {Boolean} active (false | true) Default false
29577  * @cfg {String} title title of panel
29578
29579  * 
29580  * @constructor
29581  * Create a new TabPane
29582  * @param {Object} config The config object
29583  */
29584
29585 Roo.bootstrap.dash.TabPane = function(config){
29586     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29587     
29588     this.addEvents({
29589         // raw events
29590         /**
29591          * @event activate
29592          * When a pane is activated
29593          * @param {Roo.bootstrap.dash.TabPane} pane
29594          */
29595         "activate" : true
29596          
29597     });
29598 };
29599
29600 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29601     
29602     active : false,
29603     title : '',
29604     
29605     // the tabBox that this is attached to.
29606     tab : false,
29607      
29608     getAutoCreate : function() 
29609     {
29610         var cfg = {
29611             tag: 'div',
29612             cls: 'tab-pane'
29613         };
29614         
29615         if(this.active){
29616             cfg.cls += ' active';
29617         }
29618         
29619         return cfg;
29620     },
29621     initEvents  : function()
29622     {
29623         //Roo.log('trigger add pane handler');
29624         this.parent().fireEvent('addpane', this)
29625     },
29626     
29627      /**
29628      * Updates the tab title 
29629      * @param {String} html to set the title to.
29630      */
29631     setTitle: function(str)
29632     {
29633         if (!this.tab) {
29634             return;
29635         }
29636         this.title = str;
29637         this.tab.select('a', true).first().dom.innerHTML = str;
29638         
29639     }
29640     
29641     
29642     
29643 });
29644
29645  
29646
29647
29648  /*
29649  * - LGPL
29650  *
29651  * Tooltip
29652  * 
29653  */
29654
29655 /**
29656  * @class Roo.bootstrap.Tooltip
29657  * Bootstrap Tooltip class
29658  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29659  * to determine which dom element triggers the tooltip.
29660  * 
29661  * It needs to add support for additional attributes like tooltip-position
29662  * 
29663  * @constructor
29664  * Create a new Toolti
29665  * @param {Object} config The config object
29666  */
29667
29668 Roo.bootstrap.Tooltip = function(config){
29669     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29670     
29671     this.alignment = Roo.bootstrap.Tooltip.alignment;
29672     
29673     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29674         this.alignment = config.alignment;
29675     }
29676     
29677 };
29678
29679 Roo.apply(Roo.bootstrap.Tooltip, {
29680     /**
29681      * @function init initialize tooltip monitoring.
29682      * @static
29683      */
29684     currentEl : false,
29685     currentTip : false,
29686     currentRegion : false,
29687     
29688     //  init : delay?
29689     
29690     init : function()
29691     {
29692         Roo.get(document).on('mouseover', this.enter ,this);
29693         Roo.get(document).on('mouseout', this.leave, this);
29694          
29695         
29696         this.currentTip = new Roo.bootstrap.Tooltip();
29697     },
29698     
29699     enter : function(ev)
29700     {
29701         var dom = ev.getTarget();
29702         
29703         //Roo.log(['enter',dom]);
29704         var el = Roo.fly(dom);
29705         if (this.currentEl) {
29706             //Roo.log(dom);
29707             //Roo.log(this.currentEl);
29708             //Roo.log(this.currentEl.contains(dom));
29709             if (this.currentEl == el) {
29710                 return;
29711             }
29712             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29713                 return;
29714             }
29715
29716         }
29717         
29718         if (this.currentTip.el) {
29719             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29720         }    
29721         //Roo.log(ev);
29722         
29723         if(!el || el.dom == document){
29724             return;
29725         }
29726         
29727         var bindEl = el; 
29728         var pel = false;
29729         if (!el.attr('tooltip')) {
29730             pel = el.findParent("[tooltip]");
29731             if (pel) {
29732                 bindEl = Roo.get(pel);
29733             }
29734         }
29735         
29736        
29737         
29738         // you can not look for children, as if el is the body.. then everythign is the child..
29739         if (!pel && !el.attr('tooltip')) { //
29740             if (!el.select("[tooltip]").elements.length) {
29741                 return;
29742             }
29743             // is the mouse over this child...?
29744             bindEl = el.select("[tooltip]").first();
29745             var xy = ev.getXY();
29746             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29747                 //Roo.log("not in region.");
29748                 return;
29749             }
29750             //Roo.log("child element over..");
29751             
29752         }
29753         this.currentEl = el;
29754         this.currentTip.bind(bindEl);
29755         this.currentRegion = Roo.lib.Region.getRegion(dom);
29756         this.currentTip.enter();
29757         
29758     },
29759     leave : function(ev)
29760     {
29761         var dom = ev.getTarget();
29762         //Roo.log(['leave',dom]);
29763         if (!this.currentEl) {
29764             return;
29765         }
29766         
29767         
29768         if (dom != this.currentEl.dom) {
29769             return;
29770         }
29771         var xy = ev.getXY();
29772         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29773             return;
29774         }
29775         // only activate leave if mouse cursor is outside... bounding box..
29776         
29777         
29778         
29779         
29780         if (this.currentTip) {
29781             this.currentTip.leave();
29782         }
29783         //Roo.log('clear currentEl');
29784         this.currentEl = false;
29785         
29786         
29787     },
29788     alignment : {
29789         'left' : ['r-l', [-2,0], 'right'],
29790         'right' : ['l-r', [2,0], 'left'],
29791         'bottom' : ['t-b', [0,2], 'top'],
29792         'top' : [ 'b-t', [0,-2], 'bottom']
29793     }
29794     
29795 });
29796
29797
29798 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29799     
29800     
29801     bindEl : false,
29802     
29803     delay : null, // can be { show : 300 , hide: 500}
29804     
29805     timeout : null,
29806     
29807     hoverState : null, //???
29808     
29809     placement : 'bottom', 
29810     
29811     alignment : false,
29812     
29813     getAutoCreate : function(){
29814     
29815         var cfg = {
29816            cls : 'tooltip',   
29817            role : 'tooltip',
29818            cn : [
29819                 {
29820                     cls : 'tooltip-arrow arrow'
29821                 },
29822                 {
29823                     cls : 'tooltip-inner'
29824                 }
29825            ]
29826         };
29827         
29828         return cfg;
29829     },
29830     bind : function(el)
29831     {
29832         this.bindEl = el;
29833     },
29834     
29835     initEvents : function()
29836     {
29837         this.arrowEl = this.el.select('.arrow', true).first();
29838         this.innerEl = this.el.select('.tooltip-inner', true).first();
29839     },
29840     
29841     enter : function () {
29842        
29843         if (this.timeout != null) {
29844             clearTimeout(this.timeout);
29845         }
29846         
29847         this.hoverState = 'in';
29848          //Roo.log("enter - show");
29849         if (!this.delay || !this.delay.show) {
29850             this.show();
29851             return;
29852         }
29853         var _t = this;
29854         this.timeout = setTimeout(function () {
29855             if (_t.hoverState == 'in') {
29856                 _t.show();
29857             }
29858         }, this.delay.show);
29859     },
29860     leave : function()
29861     {
29862         clearTimeout(this.timeout);
29863     
29864         this.hoverState = 'out';
29865          if (!this.delay || !this.delay.hide) {
29866             this.hide();
29867             return;
29868         }
29869        
29870         var _t = this;
29871         this.timeout = setTimeout(function () {
29872             //Roo.log("leave - timeout");
29873             
29874             if (_t.hoverState == 'out') {
29875                 _t.hide();
29876                 Roo.bootstrap.Tooltip.currentEl = false;
29877             }
29878         }, delay);
29879     },
29880     
29881     show : function (msg)
29882     {
29883         if (!this.el) {
29884             this.render(document.body);
29885         }
29886         // set content.
29887         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29888         
29889         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29890         
29891         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29892         
29893         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29894                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29895         
29896         var placement = typeof this.placement == 'function' ?
29897             this.placement.call(this, this.el, on_el) :
29898             this.placement;
29899             
29900         var autoToken = /\s?auto?\s?/i;
29901         var autoPlace = autoToken.test(placement);
29902         if (autoPlace) {
29903             placement = placement.replace(autoToken, '') || 'top';
29904         }
29905         
29906         //this.el.detach()
29907         //this.el.setXY([0,0]);
29908         this.el.show();
29909         //this.el.dom.style.display='block';
29910         
29911         //this.el.appendTo(on_el);
29912         
29913         var p = this.getPosition();
29914         var box = this.el.getBox();
29915         
29916         if (autoPlace) {
29917             // fixme..
29918         }
29919         
29920         var align = this.alignment[placement];
29921         
29922         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29923         
29924         if(placement == 'top' || placement == 'bottom'){
29925             if(xy[0] < 0){
29926                 placement = 'right';
29927             }
29928             
29929             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29930                 placement = 'left';
29931             }
29932             
29933             var scroll = Roo.select('body', true).first().getScroll();
29934             
29935             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29936                 placement = 'top';
29937             }
29938             
29939             align = this.alignment[placement];
29940             
29941             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29942             
29943         }
29944         
29945         var elems = document.getElementsByTagName('div');
29946         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29947         for (var i = 0; i < elems.length; i++) {
29948           var zindex = Number.parseInt(
29949                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29950                 10
29951           );
29952           if (zindex > highest) {
29953             highest = zindex;
29954           }
29955         }
29956         
29957         
29958         
29959         this.el.dom.style.zIndex = highest;
29960         
29961         this.el.alignTo(this.bindEl, align[0],align[1]);
29962         //var arrow = this.el.select('.arrow',true).first();
29963         //arrow.set(align[2], 
29964         
29965         this.el.addClass(placement);
29966         this.el.addClass("bs-tooltip-"+ placement);
29967         
29968         this.el.addClass('in fade show');
29969         
29970         this.hoverState = null;
29971         
29972         if (this.el.hasClass('fade')) {
29973             // fade it?
29974         }
29975         
29976         
29977         
29978         
29979         
29980     },
29981     hide : function()
29982     {
29983          
29984         if (!this.el) {
29985             return;
29986         }
29987         //this.el.setXY([0,0]);
29988         this.el.removeClass(['show', 'in']);
29989         //this.el.hide();
29990         
29991     }
29992     
29993 });
29994  
29995
29996  /*
29997  * - LGPL
29998  *
29999  * Location Picker
30000  * 
30001  */
30002
30003 /**
30004  * @class Roo.bootstrap.LocationPicker
30005  * @extends Roo.bootstrap.Component
30006  * Bootstrap LocationPicker class
30007  * @cfg {Number} latitude Position when init default 0
30008  * @cfg {Number} longitude Position when init default 0
30009  * @cfg {Number} zoom default 15
30010  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
30011  * @cfg {Boolean} mapTypeControl default false
30012  * @cfg {Boolean} disableDoubleClickZoom default false
30013  * @cfg {Boolean} scrollwheel default true
30014  * @cfg {Boolean} streetViewControl default false
30015  * @cfg {Number} radius default 0
30016  * @cfg {String} locationName
30017  * @cfg {Boolean} draggable default true
30018  * @cfg {Boolean} enableAutocomplete default false
30019  * @cfg {Boolean} enableReverseGeocode default true
30020  * @cfg {String} markerTitle
30021  * 
30022  * @constructor
30023  * Create a new LocationPicker
30024  * @param {Object} config The config object
30025  */
30026
30027
30028 Roo.bootstrap.LocationPicker = function(config){
30029     
30030     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30031     
30032     this.addEvents({
30033         /**
30034          * @event initial
30035          * Fires when the picker initialized.
30036          * @param {Roo.bootstrap.LocationPicker} this
30037          * @param {Google Location} location
30038          */
30039         initial : true,
30040         /**
30041          * @event positionchanged
30042          * Fires when the picker position changed.
30043          * @param {Roo.bootstrap.LocationPicker} this
30044          * @param {Google Location} location
30045          */
30046         positionchanged : true,
30047         /**
30048          * @event resize
30049          * Fires when the map resize.
30050          * @param {Roo.bootstrap.LocationPicker} this
30051          */
30052         resize : true,
30053         /**
30054          * @event show
30055          * Fires when the map show.
30056          * @param {Roo.bootstrap.LocationPicker} this
30057          */
30058         show : true,
30059         /**
30060          * @event hide
30061          * Fires when the map hide.
30062          * @param {Roo.bootstrap.LocationPicker} this
30063          */
30064         hide : true,
30065         /**
30066          * @event mapClick
30067          * Fires when click the map.
30068          * @param {Roo.bootstrap.LocationPicker} this
30069          * @param {Map event} e
30070          */
30071         mapClick : true,
30072         /**
30073          * @event mapRightClick
30074          * Fires when right click the map.
30075          * @param {Roo.bootstrap.LocationPicker} this
30076          * @param {Map event} e
30077          */
30078         mapRightClick : true,
30079         /**
30080          * @event markerClick
30081          * Fires when click the marker.
30082          * @param {Roo.bootstrap.LocationPicker} this
30083          * @param {Map event} e
30084          */
30085         markerClick : true,
30086         /**
30087          * @event markerRightClick
30088          * Fires when right click the marker.
30089          * @param {Roo.bootstrap.LocationPicker} this
30090          * @param {Map event} e
30091          */
30092         markerRightClick : true,
30093         /**
30094          * @event OverlayViewDraw
30095          * Fires when OverlayView Draw
30096          * @param {Roo.bootstrap.LocationPicker} this
30097          */
30098         OverlayViewDraw : true,
30099         /**
30100          * @event OverlayViewOnAdd
30101          * Fires when OverlayView Draw
30102          * @param {Roo.bootstrap.LocationPicker} this
30103          */
30104         OverlayViewOnAdd : true,
30105         /**
30106          * @event OverlayViewOnRemove
30107          * Fires when OverlayView Draw
30108          * @param {Roo.bootstrap.LocationPicker} this
30109          */
30110         OverlayViewOnRemove : true,
30111         /**
30112          * @event OverlayViewShow
30113          * Fires when OverlayView Draw
30114          * @param {Roo.bootstrap.LocationPicker} this
30115          * @param {Pixel} cpx
30116          */
30117         OverlayViewShow : true,
30118         /**
30119          * @event OverlayViewHide
30120          * Fires when OverlayView Draw
30121          * @param {Roo.bootstrap.LocationPicker} this
30122          */
30123         OverlayViewHide : true,
30124         /**
30125          * @event loadexception
30126          * Fires when load google lib failed.
30127          * @param {Roo.bootstrap.LocationPicker} this
30128          */
30129         loadexception : true
30130     });
30131         
30132 };
30133
30134 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30135     
30136     gMapContext: false,
30137     
30138     latitude: 0,
30139     longitude: 0,
30140     zoom: 15,
30141     mapTypeId: false,
30142     mapTypeControl: false,
30143     disableDoubleClickZoom: false,
30144     scrollwheel: true,
30145     streetViewControl: false,
30146     radius: 0,
30147     locationName: '',
30148     draggable: true,
30149     enableAutocomplete: false,
30150     enableReverseGeocode: true,
30151     markerTitle: '',
30152     
30153     getAutoCreate: function()
30154     {
30155
30156         var cfg = {
30157             tag: 'div',
30158             cls: 'roo-location-picker'
30159         };
30160         
30161         return cfg
30162     },
30163     
30164     initEvents: function(ct, position)
30165     {       
30166         if(!this.el.getWidth() || this.isApplied()){
30167             return;
30168         }
30169         
30170         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30171         
30172         this.initial();
30173     },
30174     
30175     initial: function()
30176     {
30177         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30178             this.fireEvent('loadexception', this);
30179             return;
30180         }
30181         
30182         if(!this.mapTypeId){
30183             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30184         }
30185         
30186         this.gMapContext = this.GMapContext();
30187         
30188         this.initOverlayView();
30189         
30190         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30191         
30192         var _this = this;
30193                 
30194         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30195             _this.setPosition(_this.gMapContext.marker.position);
30196         });
30197         
30198         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30199             _this.fireEvent('mapClick', this, event);
30200             
30201         });
30202
30203         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30204             _this.fireEvent('mapRightClick', this, event);
30205             
30206         });
30207         
30208         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30209             _this.fireEvent('markerClick', this, event);
30210             
30211         });
30212
30213         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30214             _this.fireEvent('markerRightClick', this, event);
30215             
30216         });
30217         
30218         this.setPosition(this.gMapContext.location);
30219         
30220         this.fireEvent('initial', this, this.gMapContext.location);
30221     },
30222     
30223     initOverlayView: function()
30224     {
30225         var _this = this;
30226         
30227         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30228             
30229             draw: function()
30230             {
30231                 _this.fireEvent('OverlayViewDraw', _this);
30232             },
30233             
30234             onAdd: function()
30235             {
30236                 _this.fireEvent('OverlayViewOnAdd', _this);
30237             },
30238             
30239             onRemove: function()
30240             {
30241                 _this.fireEvent('OverlayViewOnRemove', _this);
30242             },
30243             
30244             show: function(cpx)
30245             {
30246                 _this.fireEvent('OverlayViewShow', _this, cpx);
30247             },
30248             
30249             hide: function()
30250             {
30251                 _this.fireEvent('OverlayViewHide', _this);
30252             }
30253             
30254         });
30255     },
30256     
30257     fromLatLngToContainerPixel: function(event)
30258     {
30259         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30260     },
30261     
30262     isApplied: function() 
30263     {
30264         return this.getGmapContext() == false ? false : true;
30265     },
30266     
30267     getGmapContext: function() 
30268     {
30269         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30270     },
30271     
30272     GMapContext: function() 
30273     {
30274         var position = new google.maps.LatLng(this.latitude, this.longitude);
30275         
30276         var _map = new google.maps.Map(this.el.dom, {
30277             center: position,
30278             zoom: this.zoom,
30279             mapTypeId: this.mapTypeId,
30280             mapTypeControl: this.mapTypeControl,
30281             disableDoubleClickZoom: this.disableDoubleClickZoom,
30282             scrollwheel: this.scrollwheel,
30283             streetViewControl: this.streetViewControl,
30284             locationName: this.locationName,
30285             draggable: this.draggable,
30286             enableAutocomplete: this.enableAutocomplete,
30287             enableReverseGeocode: this.enableReverseGeocode
30288         });
30289         
30290         var _marker = new google.maps.Marker({
30291             position: position,
30292             map: _map,
30293             title: this.markerTitle,
30294             draggable: this.draggable
30295         });
30296         
30297         return {
30298             map: _map,
30299             marker: _marker,
30300             circle: null,
30301             location: position,
30302             radius: this.radius,
30303             locationName: this.locationName,
30304             addressComponents: {
30305                 formatted_address: null,
30306                 addressLine1: null,
30307                 addressLine2: null,
30308                 streetName: null,
30309                 streetNumber: null,
30310                 city: null,
30311                 district: null,
30312                 state: null,
30313                 stateOrProvince: null
30314             },
30315             settings: this,
30316             domContainer: this.el.dom,
30317             geodecoder: new google.maps.Geocoder()
30318         };
30319     },
30320     
30321     drawCircle: function(center, radius, options) 
30322     {
30323         if (this.gMapContext.circle != null) {
30324             this.gMapContext.circle.setMap(null);
30325         }
30326         if (radius > 0) {
30327             radius *= 1;
30328             options = Roo.apply({}, options, {
30329                 strokeColor: "#0000FF",
30330                 strokeOpacity: .35,
30331                 strokeWeight: 2,
30332                 fillColor: "#0000FF",
30333                 fillOpacity: .2
30334             });
30335             
30336             options.map = this.gMapContext.map;
30337             options.radius = radius;
30338             options.center = center;
30339             this.gMapContext.circle = new google.maps.Circle(options);
30340             return this.gMapContext.circle;
30341         }
30342         
30343         return null;
30344     },
30345     
30346     setPosition: function(location) 
30347     {
30348         this.gMapContext.location = location;
30349         this.gMapContext.marker.setPosition(location);
30350         this.gMapContext.map.panTo(location);
30351         this.drawCircle(location, this.gMapContext.radius, {});
30352         
30353         var _this = this;
30354         
30355         if (this.gMapContext.settings.enableReverseGeocode) {
30356             this.gMapContext.geodecoder.geocode({
30357                 latLng: this.gMapContext.location
30358             }, function(results, status) {
30359                 
30360                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30361                     _this.gMapContext.locationName = results[0].formatted_address;
30362                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30363                     
30364                     _this.fireEvent('positionchanged', this, location);
30365                 }
30366             });
30367             
30368             return;
30369         }
30370         
30371         this.fireEvent('positionchanged', this, location);
30372     },
30373     
30374     resize: function()
30375     {
30376         google.maps.event.trigger(this.gMapContext.map, "resize");
30377         
30378         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30379         
30380         this.fireEvent('resize', this);
30381     },
30382     
30383     setPositionByLatLng: function(latitude, longitude)
30384     {
30385         this.setPosition(new google.maps.LatLng(latitude, longitude));
30386     },
30387     
30388     getCurrentPosition: function() 
30389     {
30390         return {
30391             latitude: this.gMapContext.location.lat(),
30392             longitude: this.gMapContext.location.lng()
30393         };
30394     },
30395     
30396     getAddressName: function() 
30397     {
30398         return this.gMapContext.locationName;
30399     },
30400     
30401     getAddressComponents: function() 
30402     {
30403         return this.gMapContext.addressComponents;
30404     },
30405     
30406     address_component_from_google_geocode: function(address_components) 
30407     {
30408         var result = {};
30409         
30410         for (var i = 0; i < address_components.length; i++) {
30411             var component = address_components[i];
30412             if (component.types.indexOf("postal_code") >= 0) {
30413                 result.postalCode = component.short_name;
30414             } else if (component.types.indexOf("street_number") >= 0) {
30415                 result.streetNumber = component.short_name;
30416             } else if (component.types.indexOf("route") >= 0) {
30417                 result.streetName = component.short_name;
30418             } else if (component.types.indexOf("neighborhood") >= 0) {
30419                 result.city = component.short_name;
30420             } else if (component.types.indexOf("locality") >= 0) {
30421                 result.city = component.short_name;
30422             } else if (component.types.indexOf("sublocality") >= 0) {
30423                 result.district = component.short_name;
30424             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30425                 result.stateOrProvince = component.short_name;
30426             } else if (component.types.indexOf("country") >= 0) {
30427                 result.country = component.short_name;
30428             }
30429         }
30430         
30431         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30432         result.addressLine2 = "";
30433         return result;
30434     },
30435     
30436     setZoomLevel: function(zoom)
30437     {
30438         this.gMapContext.map.setZoom(zoom);
30439     },
30440     
30441     show: function()
30442     {
30443         if(!this.el){
30444             return;
30445         }
30446         
30447         this.el.show();
30448         
30449         this.resize();
30450         
30451         this.fireEvent('show', this);
30452     },
30453     
30454     hide: function()
30455     {
30456         if(!this.el){
30457             return;
30458         }
30459         
30460         this.el.hide();
30461         
30462         this.fireEvent('hide', this);
30463     }
30464     
30465 });
30466
30467 Roo.apply(Roo.bootstrap.LocationPicker, {
30468     
30469     OverlayView : function(map, options)
30470     {
30471         options = options || {};
30472         
30473         this.setMap(map);
30474     }
30475     
30476     
30477 });/**
30478  * @class Roo.bootstrap.Alert
30479  * @extends Roo.bootstrap.Component
30480  * Bootstrap Alert class - shows an alert area box
30481  * eg
30482  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30483   Enter a valid email address
30484 </div>
30485  * @licence LGPL
30486  * @cfg {String} title The title of alert
30487  * @cfg {String} html The content of alert
30488  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30489  * @cfg {String} fa font-awesomeicon
30490  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30491  * @cfg {Boolean} close true to show a x closer
30492  * 
30493  * 
30494  * @constructor
30495  * Create a new alert
30496  * @param {Object} config The config object
30497  */
30498
30499
30500 Roo.bootstrap.Alert = function(config){
30501     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30502     
30503 };
30504
30505 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30506     
30507     title: '',
30508     html: '',
30509     weight: false,
30510     fa: false,
30511     faicon: false, // BC
30512     close : false,
30513     
30514     
30515     getAutoCreate : function()
30516     {
30517         
30518         var cfg = {
30519             tag : 'div',
30520             cls : 'alert',
30521             cn : [
30522                 {
30523                     tag: 'button',
30524                     type :  "button",
30525                     cls: "close",
30526                     html : '×',
30527                     style : this.close ? '' : 'display:none'
30528                 },
30529                 {
30530                     tag : 'i',
30531                     cls : 'roo-alert-icon'
30532                     
30533                 },
30534                 {
30535                     tag : 'b',
30536                     cls : 'roo-alert-title',
30537                     html : this.title
30538                 },
30539                 {
30540                     tag : 'span',
30541                     cls : 'roo-alert-text',
30542                     html : this.html
30543                 }
30544             ]
30545         };
30546         
30547         if(this.faicon){
30548             cfg.cn[0].cls += ' fa ' + this.faicon;
30549         }
30550         if(this.fa){
30551             cfg.cn[0].cls += ' fa ' + this.fa;
30552         }
30553         
30554         if(this.weight){
30555             cfg.cls += ' alert-' + this.weight;
30556         }
30557         
30558         return cfg;
30559     },
30560     
30561     initEvents: function() 
30562     {
30563         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30564         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30565         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30566         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30567         if (this.seconds > 0) {
30568             this.hide.defer(this.seconds, this);
30569         }
30570     },
30571     /**
30572      * Set the Title Message HTML
30573      * @param {String} html
30574      */
30575     setTitle : function(str)
30576     {
30577         this.titleEl.dom.innerHTML = str;
30578     },
30579      
30580      /**
30581      * Set the Body Message HTML
30582      * @param {String} html
30583      */
30584     setHtml : function(str)
30585     {
30586         this.htmlEl.dom.innerHTML = str;
30587     },
30588     /**
30589      * Set the Weight of the alert
30590      * @param {String} (success|info|warning|danger) weight
30591      */
30592     
30593     setWeight : function(weight)
30594     {
30595         if(this.weight){
30596             this.el.removeClass('alert-' + this.weight);
30597         }
30598         
30599         this.weight = weight;
30600         
30601         this.el.addClass('alert-' + this.weight);
30602     },
30603       /**
30604      * Set the Icon of the alert
30605      * @param {String} see fontawsome names (name without the 'fa-' bit)
30606      */
30607     setIcon : function(icon)
30608     {
30609         if(this.faicon){
30610             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30611         }
30612         
30613         this.faicon = icon;
30614         
30615         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30616     },
30617     /**
30618      * Hide the Alert
30619      */
30620     hide: function() 
30621     {
30622         this.el.hide();   
30623     },
30624     /**
30625      * Show the Alert
30626      */
30627     show: function() 
30628     {  
30629         this.el.show();   
30630     }
30631     
30632 });
30633
30634  
30635 /*
30636 * Licence: LGPL
30637 */
30638
30639 /**
30640  * @class Roo.bootstrap.UploadCropbox
30641  * @extends Roo.bootstrap.Component
30642  * Bootstrap UploadCropbox class
30643  * @cfg {String} emptyText show when image has been loaded
30644  * @cfg {String} rotateNotify show when image too small to rotate
30645  * @cfg {Number} errorTimeout default 3000
30646  * @cfg {Number} minWidth default 300
30647  * @cfg {Number} minHeight default 300
30648  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30649  * @cfg {Boolean} isDocument (true|false) default false
30650  * @cfg {String} url action url
30651  * @cfg {String} paramName default 'imageUpload'
30652  * @cfg {String} method default POST
30653  * @cfg {Boolean} loadMask (true|false) default true
30654  * @cfg {Boolean} loadingText default 'Loading...'
30655  * 
30656  * @constructor
30657  * Create a new UploadCropbox
30658  * @param {Object} config The config object
30659  */
30660
30661 Roo.bootstrap.UploadCropbox = function(config){
30662     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30663     
30664     this.addEvents({
30665         /**
30666          * @event beforeselectfile
30667          * Fire before select file
30668          * @param {Roo.bootstrap.UploadCropbox} this
30669          */
30670         "beforeselectfile" : true,
30671         /**
30672          * @event initial
30673          * Fire after initEvent
30674          * @param {Roo.bootstrap.UploadCropbox} this
30675          */
30676         "initial" : true,
30677         /**
30678          * @event crop
30679          * Fire after initEvent
30680          * @param {Roo.bootstrap.UploadCropbox} this
30681          * @param {String} data
30682          */
30683         "crop" : true,
30684         /**
30685          * @event prepare
30686          * Fire when preparing the file data
30687          * @param {Roo.bootstrap.UploadCropbox} this
30688          * @param {Object} file
30689          */
30690         "prepare" : true,
30691         /**
30692          * @event exception
30693          * Fire when get exception
30694          * @param {Roo.bootstrap.UploadCropbox} this
30695          * @param {XMLHttpRequest} xhr
30696          */
30697         "exception" : true,
30698         /**
30699          * @event beforeloadcanvas
30700          * Fire before load the canvas
30701          * @param {Roo.bootstrap.UploadCropbox} this
30702          * @param {String} src
30703          */
30704         "beforeloadcanvas" : true,
30705         /**
30706          * @event trash
30707          * Fire when trash image
30708          * @param {Roo.bootstrap.UploadCropbox} this
30709          */
30710         "trash" : true,
30711         /**
30712          * @event download
30713          * Fire when download the image
30714          * @param {Roo.bootstrap.UploadCropbox} this
30715          */
30716         "download" : true,
30717         /**
30718          * @event footerbuttonclick
30719          * Fire when footerbuttonclick
30720          * @param {Roo.bootstrap.UploadCropbox} this
30721          * @param {String} type
30722          */
30723         "footerbuttonclick" : true,
30724         /**
30725          * @event resize
30726          * Fire when resize
30727          * @param {Roo.bootstrap.UploadCropbox} this
30728          */
30729         "resize" : true,
30730         /**
30731          * @event rotate
30732          * Fire when rotate the image
30733          * @param {Roo.bootstrap.UploadCropbox} this
30734          * @param {String} pos
30735          */
30736         "rotate" : true,
30737         /**
30738          * @event inspect
30739          * Fire when inspect the file
30740          * @param {Roo.bootstrap.UploadCropbox} this
30741          * @param {Object} file
30742          */
30743         "inspect" : true,
30744         /**
30745          * @event upload
30746          * Fire when xhr upload the file
30747          * @param {Roo.bootstrap.UploadCropbox} this
30748          * @param {Object} data
30749          */
30750         "upload" : true,
30751         /**
30752          * @event arrange
30753          * Fire when arrange the file data
30754          * @param {Roo.bootstrap.UploadCropbox} this
30755          * @param {Object} formData
30756          */
30757         "arrange" : true
30758     });
30759     
30760     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30761 };
30762
30763 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30764     
30765     emptyText : 'Click to upload image',
30766     rotateNotify : 'Image is too small to rotate',
30767     errorTimeout : 3000,
30768     scale : 0,
30769     baseScale : 1,
30770     rotate : 0,
30771     dragable : false,
30772     pinching : false,
30773     mouseX : 0,
30774     mouseY : 0,
30775     cropData : false,
30776     minWidth : 300,
30777     minHeight : 300,
30778     file : false,
30779     exif : {},
30780     baseRotate : 1,
30781     cropType : 'image/jpeg',
30782     buttons : false,
30783     canvasLoaded : false,
30784     isDocument : false,
30785     method : 'POST',
30786     paramName : 'imageUpload',
30787     loadMask : true,
30788     loadingText : 'Loading...',
30789     maskEl : false,
30790     
30791     getAutoCreate : function()
30792     {
30793         var cfg = {
30794             tag : 'div',
30795             cls : 'roo-upload-cropbox',
30796             cn : [
30797                 {
30798                     tag : 'input',
30799                     cls : 'roo-upload-cropbox-selector',
30800                     type : 'file'
30801                 },
30802                 {
30803                     tag : 'div',
30804                     cls : 'roo-upload-cropbox-body',
30805                     style : 'cursor:pointer',
30806                     cn : [
30807                         {
30808                             tag : 'div',
30809                             cls : 'roo-upload-cropbox-preview'
30810                         },
30811                         {
30812                             tag : 'div',
30813                             cls : 'roo-upload-cropbox-thumb'
30814                         },
30815                         {
30816                             tag : 'div',
30817                             cls : 'roo-upload-cropbox-empty-notify',
30818                             html : this.emptyText
30819                         },
30820                         {
30821                             tag : 'div',
30822                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30823                             html : this.rotateNotify
30824                         }
30825                     ]
30826                 },
30827                 {
30828                     tag : 'div',
30829                     cls : 'roo-upload-cropbox-footer',
30830                     cn : {
30831                         tag : 'div',
30832                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30833                         cn : []
30834                     }
30835                 }
30836             ]
30837         };
30838         
30839         return cfg;
30840     },
30841     
30842     onRender : function(ct, position)
30843     {
30844         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30845         
30846         if (this.buttons.length) {
30847             
30848             Roo.each(this.buttons, function(bb) {
30849                 
30850                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30851                 
30852                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30853                 
30854             }, this);
30855         }
30856         
30857         if(this.loadMask){
30858             this.maskEl = this.el;
30859         }
30860     },
30861     
30862     initEvents : function()
30863     {
30864         this.urlAPI = (window.createObjectURL && window) || 
30865                                 (window.URL && URL.revokeObjectURL && URL) || 
30866                                 (window.webkitURL && webkitURL);
30867                         
30868         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30869         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30870         
30871         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30872         this.selectorEl.hide();
30873         
30874         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30875         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30876         
30877         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30878         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30879         this.thumbEl.hide();
30880         
30881         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30882         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30883         
30884         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30885         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30886         this.errorEl.hide();
30887         
30888         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30889         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30890         this.footerEl.hide();
30891         
30892         this.setThumbBoxSize();
30893         
30894         this.bind();
30895         
30896         this.resize();
30897         
30898         this.fireEvent('initial', this);
30899     },
30900
30901     bind : function()
30902     {
30903         var _this = this;
30904         
30905         window.addEventListener("resize", function() { _this.resize(); } );
30906         
30907         this.bodyEl.on('click', this.beforeSelectFile, this);
30908         
30909         if(Roo.isTouch){
30910             this.bodyEl.on('touchstart', this.onTouchStart, this);
30911             this.bodyEl.on('touchmove', this.onTouchMove, this);
30912             this.bodyEl.on('touchend', this.onTouchEnd, this);
30913         }
30914         
30915         if(!Roo.isTouch){
30916             this.bodyEl.on('mousedown', this.onMouseDown, this);
30917             this.bodyEl.on('mousemove', this.onMouseMove, this);
30918             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30919             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30920             Roo.get(document).on('mouseup', this.onMouseUp, this);
30921         }
30922         
30923         this.selectorEl.on('change', this.onFileSelected, this);
30924     },
30925     
30926     reset : function()
30927     {    
30928         this.scale = 0;
30929         this.baseScale = 1;
30930         this.rotate = 0;
30931         this.baseRotate = 1;
30932         this.dragable = false;
30933         this.pinching = false;
30934         this.mouseX = 0;
30935         this.mouseY = 0;
30936         this.cropData = false;
30937         this.notifyEl.dom.innerHTML = this.emptyText;
30938         
30939         this.selectorEl.dom.value = '';
30940         
30941     },
30942     
30943     resize : function()
30944     {
30945         if(this.fireEvent('resize', this) != false){
30946             this.setThumbBoxPosition();
30947             this.setCanvasPosition();
30948         }
30949     },
30950     
30951     onFooterButtonClick : function(e, el, o, type)
30952     {
30953         switch (type) {
30954             case 'rotate-left' :
30955                 this.onRotateLeft(e);
30956                 break;
30957             case 'rotate-right' :
30958                 this.onRotateRight(e);
30959                 break;
30960             case 'picture' :
30961                 this.beforeSelectFile(e);
30962                 break;
30963             case 'trash' :
30964                 this.trash(e);
30965                 break;
30966             case 'crop' :
30967                 this.crop(e);
30968                 break;
30969             case 'download' :
30970                 this.download(e);
30971                 break;
30972             default :
30973                 break;
30974         }
30975         
30976         this.fireEvent('footerbuttonclick', this, type);
30977     },
30978     
30979     beforeSelectFile : function(e)
30980     {
30981         e.preventDefault();
30982         
30983         if(this.fireEvent('beforeselectfile', this) != false){
30984             this.selectorEl.dom.click();
30985         }
30986     },
30987     
30988     onFileSelected : function(e)
30989     {
30990         e.preventDefault();
30991         
30992         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30993             return;
30994         }
30995         
30996         var file = this.selectorEl.dom.files[0];
30997         
30998         if(this.fireEvent('inspect', this, file) != false){
30999             this.prepare(file);
31000         }
31001         
31002     },
31003     
31004     trash : function(e)
31005     {
31006         this.fireEvent('trash', this);
31007     },
31008     
31009     download : function(e)
31010     {
31011         this.fireEvent('download', this);
31012     },
31013     
31014     loadCanvas : function(src)
31015     {   
31016         if(this.fireEvent('beforeloadcanvas', this, src) != false){
31017             
31018             this.reset();
31019             
31020             this.imageEl = document.createElement('img');
31021             
31022             var _this = this;
31023             
31024             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31025             
31026             this.imageEl.src = src;
31027         }
31028     },
31029     
31030     onLoadCanvas : function()
31031     {   
31032         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31033         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31034         
31035         this.bodyEl.un('click', this.beforeSelectFile, this);
31036         
31037         this.notifyEl.hide();
31038         this.thumbEl.show();
31039         this.footerEl.show();
31040         
31041         this.baseRotateLevel();
31042         
31043         if(this.isDocument){
31044             this.setThumbBoxSize();
31045         }
31046         
31047         this.setThumbBoxPosition();
31048         
31049         this.baseScaleLevel();
31050         
31051         this.draw();
31052         
31053         this.resize();
31054         
31055         this.canvasLoaded = true;
31056         
31057         if(this.loadMask){
31058             this.maskEl.unmask();
31059         }
31060         
31061     },
31062     
31063     setCanvasPosition : function()
31064     {   
31065         if(!this.canvasEl){
31066             return;
31067         }
31068         
31069         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31070         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31071         
31072         this.previewEl.setLeft(pw);
31073         this.previewEl.setTop(ph);
31074         
31075     },
31076     
31077     onMouseDown : function(e)
31078     {   
31079         e.stopEvent();
31080         
31081         this.dragable = true;
31082         this.pinching = false;
31083         
31084         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31085             this.dragable = false;
31086             return;
31087         }
31088         
31089         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31090         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31091         
31092     },
31093     
31094     onMouseMove : function(e)
31095     {   
31096         e.stopEvent();
31097         
31098         if(!this.canvasLoaded){
31099             return;
31100         }
31101         
31102         if (!this.dragable){
31103             return;
31104         }
31105         
31106         var minX = Math.ceil(this.thumbEl.getLeft(true));
31107         var minY = Math.ceil(this.thumbEl.getTop(true));
31108         
31109         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31110         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31111         
31112         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31113         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31114         
31115         x = x - this.mouseX;
31116         y = y - this.mouseY;
31117         
31118         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31119         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31120         
31121         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31122         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31123         
31124         this.previewEl.setLeft(bgX);
31125         this.previewEl.setTop(bgY);
31126         
31127         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31128         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31129     },
31130     
31131     onMouseUp : function(e)
31132     {   
31133         e.stopEvent();
31134         
31135         this.dragable = false;
31136     },
31137     
31138     onMouseWheel : function(e)
31139     {   
31140         e.stopEvent();
31141         
31142         this.startScale = this.scale;
31143         
31144         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31145         
31146         if(!this.zoomable()){
31147             this.scale = this.startScale;
31148             return;
31149         }
31150         
31151         this.draw();
31152         
31153         return;
31154     },
31155     
31156     zoomable : function()
31157     {
31158         var minScale = this.thumbEl.getWidth() / this.minWidth;
31159         
31160         if(this.minWidth < this.minHeight){
31161             minScale = this.thumbEl.getHeight() / this.minHeight;
31162         }
31163         
31164         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31165         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31166         
31167         if(
31168                 this.isDocument &&
31169                 (this.rotate == 0 || this.rotate == 180) && 
31170                 (
31171                     width > this.imageEl.OriginWidth || 
31172                     height > this.imageEl.OriginHeight ||
31173                     (width < this.minWidth && height < this.minHeight)
31174                 )
31175         ){
31176             return false;
31177         }
31178         
31179         if(
31180                 this.isDocument &&
31181                 (this.rotate == 90 || this.rotate == 270) && 
31182                 (
31183                     width > this.imageEl.OriginWidth || 
31184                     height > this.imageEl.OriginHeight ||
31185                     (width < this.minHeight && height < this.minWidth)
31186                 )
31187         ){
31188             return false;
31189         }
31190         
31191         if(
31192                 !this.isDocument &&
31193                 (this.rotate == 0 || this.rotate == 180) && 
31194                 (
31195                     width < this.minWidth || 
31196                     width > this.imageEl.OriginWidth || 
31197                     height < this.minHeight || 
31198                     height > this.imageEl.OriginHeight
31199                 )
31200         ){
31201             return false;
31202         }
31203         
31204         if(
31205                 !this.isDocument &&
31206                 (this.rotate == 90 || this.rotate == 270) && 
31207                 (
31208                     width < this.minHeight || 
31209                     width > this.imageEl.OriginWidth || 
31210                     height < this.minWidth || 
31211                     height > this.imageEl.OriginHeight
31212                 )
31213         ){
31214             return false;
31215         }
31216         
31217         return true;
31218         
31219     },
31220     
31221     onRotateLeft : function(e)
31222     {   
31223         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31224             
31225             var minScale = this.thumbEl.getWidth() / this.minWidth;
31226             
31227             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31228             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31229             
31230             this.startScale = this.scale;
31231             
31232             while (this.getScaleLevel() < minScale){
31233             
31234                 this.scale = this.scale + 1;
31235                 
31236                 if(!this.zoomable()){
31237                     break;
31238                 }
31239                 
31240                 if(
31241                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31242                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31243                 ){
31244                     continue;
31245                 }
31246                 
31247                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31248
31249                 this.draw();
31250                 
31251                 return;
31252             }
31253             
31254             this.scale = this.startScale;
31255             
31256             this.onRotateFail();
31257             
31258             return false;
31259         }
31260         
31261         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31262
31263         if(this.isDocument){
31264             this.setThumbBoxSize();
31265             this.setThumbBoxPosition();
31266             this.setCanvasPosition();
31267         }
31268         
31269         this.draw();
31270         
31271         this.fireEvent('rotate', this, 'left');
31272         
31273     },
31274     
31275     onRotateRight : function(e)
31276     {
31277         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31278             
31279             var minScale = this.thumbEl.getWidth() / this.minWidth;
31280         
31281             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31282             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31283             
31284             this.startScale = this.scale;
31285             
31286             while (this.getScaleLevel() < minScale){
31287             
31288                 this.scale = this.scale + 1;
31289                 
31290                 if(!this.zoomable()){
31291                     break;
31292                 }
31293                 
31294                 if(
31295                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31296                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31297                 ){
31298                     continue;
31299                 }
31300                 
31301                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31302
31303                 this.draw();
31304                 
31305                 return;
31306             }
31307             
31308             this.scale = this.startScale;
31309             
31310             this.onRotateFail();
31311             
31312             return false;
31313         }
31314         
31315         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31316
31317         if(this.isDocument){
31318             this.setThumbBoxSize();
31319             this.setThumbBoxPosition();
31320             this.setCanvasPosition();
31321         }
31322         
31323         this.draw();
31324         
31325         this.fireEvent('rotate', this, 'right');
31326     },
31327     
31328     onRotateFail : function()
31329     {
31330         this.errorEl.show(true);
31331         
31332         var _this = this;
31333         
31334         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31335     },
31336     
31337     draw : function()
31338     {
31339         this.previewEl.dom.innerHTML = '';
31340         
31341         var canvasEl = document.createElement("canvas");
31342         
31343         var contextEl = canvasEl.getContext("2d");
31344         
31345         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31346         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31347         var center = this.imageEl.OriginWidth / 2;
31348         
31349         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31350             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31351             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31352             center = this.imageEl.OriginHeight / 2;
31353         }
31354         
31355         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31356         
31357         contextEl.translate(center, center);
31358         contextEl.rotate(this.rotate * Math.PI / 180);
31359
31360         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31361         
31362         this.canvasEl = document.createElement("canvas");
31363         
31364         this.contextEl = this.canvasEl.getContext("2d");
31365         
31366         switch (this.rotate) {
31367             case 0 :
31368                 
31369                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31370                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31371                 
31372                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31373                 
31374                 break;
31375             case 90 : 
31376                 
31377                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31378                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31379                 
31380                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31381                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31382                     break;
31383                 }
31384                 
31385                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31386                 
31387                 break;
31388             case 180 :
31389                 
31390                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31391                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31392                 
31393                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31394                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31395                     break;
31396                 }
31397                 
31398                 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);
31399                 
31400                 break;
31401             case 270 :
31402                 
31403                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31404                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31405         
31406                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31407                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31408                     break;
31409                 }
31410                 
31411                 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);
31412                 
31413                 break;
31414             default : 
31415                 break;
31416         }
31417         
31418         this.previewEl.appendChild(this.canvasEl);
31419         
31420         this.setCanvasPosition();
31421     },
31422     
31423     crop : function()
31424     {
31425         if(!this.canvasLoaded){
31426             return;
31427         }
31428         
31429         var imageCanvas = document.createElement("canvas");
31430         
31431         var imageContext = imageCanvas.getContext("2d");
31432         
31433         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31434         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31435         
31436         var center = imageCanvas.width / 2;
31437         
31438         imageContext.translate(center, center);
31439         
31440         imageContext.rotate(this.rotate * Math.PI / 180);
31441         
31442         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31443         
31444         var canvas = document.createElement("canvas");
31445         
31446         var context = canvas.getContext("2d");
31447                 
31448         canvas.width = this.minWidth;
31449         canvas.height = this.minHeight;
31450
31451         switch (this.rotate) {
31452             case 0 :
31453                 
31454                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31455                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31456                 
31457                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31458                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31459                 
31460                 var targetWidth = this.minWidth - 2 * x;
31461                 var targetHeight = this.minHeight - 2 * y;
31462                 
31463                 var scale = 1;
31464                 
31465                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31466                     scale = targetWidth / width;
31467                 }
31468                 
31469                 if(x > 0 && y == 0){
31470                     scale = targetHeight / height;
31471                 }
31472                 
31473                 if(x > 0 && y > 0){
31474                     scale = targetWidth / width;
31475                     
31476                     if(width < height){
31477                         scale = targetHeight / height;
31478                     }
31479                 }
31480                 
31481                 context.scale(scale, scale);
31482                 
31483                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31484                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31485
31486                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31487                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31488
31489                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31490                 
31491                 break;
31492             case 90 : 
31493                 
31494                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31495                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31496                 
31497                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31498                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31499                 
31500                 var targetWidth = this.minWidth - 2 * x;
31501                 var targetHeight = this.minHeight - 2 * y;
31502                 
31503                 var scale = 1;
31504                 
31505                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31506                     scale = targetWidth / width;
31507                 }
31508                 
31509                 if(x > 0 && y == 0){
31510                     scale = targetHeight / height;
31511                 }
31512                 
31513                 if(x > 0 && y > 0){
31514                     scale = targetWidth / width;
31515                     
31516                     if(width < height){
31517                         scale = targetHeight / height;
31518                     }
31519                 }
31520                 
31521                 context.scale(scale, scale);
31522                 
31523                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31524                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31525
31526                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31527                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31528                 
31529                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31530                 
31531                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31532                 
31533                 break;
31534             case 180 :
31535                 
31536                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31537                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31538                 
31539                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31540                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31541                 
31542                 var targetWidth = this.minWidth - 2 * x;
31543                 var targetHeight = this.minHeight - 2 * y;
31544                 
31545                 var scale = 1;
31546                 
31547                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31548                     scale = targetWidth / width;
31549                 }
31550                 
31551                 if(x > 0 && y == 0){
31552                     scale = targetHeight / height;
31553                 }
31554                 
31555                 if(x > 0 && y > 0){
31556                     scale = targetWidth / width;
31557                     
31558                     if(width < height){
31559                         scale = targetHeight / height;
31560                     }
31561                 }
31562                 
31563                 context.scale(scale, scale);
31564                 
31565                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31566                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31567
31568                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31569                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31570
31571                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31572                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31573                 
31574                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31575                 
31576                 break;
31577             case 270 :
31578                 
31579                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31580                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31581                 
31582                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31583                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31584                 
31585                 var targetWidth = this.minWidth - 2 * x;
31586                 var targetHeight = this.minHeight - 2 * y;
31587                 
31588                 var scale = 1;
31589                 
31590                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31591                     scale = targetWidth / width;
31592                 }
31593                 
31594                 if(x > 0 && y == 0){
31595                     scale = targetHeight / height;
31596                 }
31597                 
31598                 if(x > 0 && y > 0){
31599                     scale = targetWidth / width;
31600                     
31601                     if(width < height){
31602                         scale = targetHeight / height;
31603                     }
31604                 }
31605                 
31606                 context.scale(scale, scale);
31607                 
31608                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31609                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31610
31611                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31612                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31613                 
31614                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31615                 
31616                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31617                 
31618                 break;
31619             default : 
31620                 break;
31621         }
31622         
31623         this.cropData = canvas.toDataURL(this.cropType);
31624         
31625         if(this.fireEvent('crop', this, this.cropData) !== false){
31626             this.process(this.file, this.cropData);
31627         }
31628         
31629         return;
31630         
31631     },
31632     
31633     setThumbBoxSize : function()
31634     {
31635         var width, height;
31636         
31637         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31638             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31639             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31640             
31641             this.minWidth = width;
31642             this.minHeight = height;
31643             
31644             if(this.rotate == 90 || this.rotate == 270){
31645                 this.minWidth = height;
31646                 this.minHeight = width;
31647             }
31648         }
31649         
31650         height = 300;
31651         width = Math.ceil(this.minWidth * height / this.minHeight);
31652         
31653         if(this.minWidth > this.minHeight){
31654             width = 300;
31655             height = Math.ceil(this.minHeight * width / this.minWidth);
31656         }
31657         
31658         this.thumbEl.setStyle({
31659             width : width + 'px',
31660             height : height + 'px'
31661         });
31662
31663         return;
31664             
31665     },
31666     
31667     setThumbBoxPosition : function()
31668     {
31669         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31670         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31671         
31672         this.thumbEl.setLeft(x);
31673         this.thumbEl.setTop(y);
31674         
31675     },
31676     
31677     baseRotateLevel : function()
31678     {
31679         this.baseRotate = 1;
31680         
31681         if(
31682                 typeof(this.exif) != 'undefined' &&
31683                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31684                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31685         ){
31686             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31687         }
31688         
31689         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31690         
31691     },
31692     
31693     baseScaleLevel : function()
31694     {
31695         var width, height;
31696         
31697         if(this.isDocument){
31698             
31699             if(this.baseRotate == 6 || this.baseRotate == 8){
31700             
31701                 height = this.thumbEl.getHeight();
31702                 this.baseScale = height / this.imageEl.OriginWidth;
31703
31704                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31705                     width = this.thumbEl.getWidth();
31706                     this.baseScale = width / this.imageEl.OriginHeight;
31707                 }
31708
31709                 return;
31710             }
31711
31712             height = this.thumbEl.getHeight();
31713             this.baseScale = height / this.imageEl.OriginHeight;
31714
31715             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31716                 width = this.thumbEl.getWidth();
31717                 this.baseScale = width / this.imageEl.OriginWidth;
31718             }
31719
31720             return;
31721         }
31722         
31723         if(this.baseRotate == 6 || this.baseRotate == 8){
31724             
31725             width = this.thumbEl.getHeight();
31726             this.baseScale = width / this.imageEl.OriginHeight;
31727             
31728             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31729                 height = this.thumbEl.getWidth();
31730                 this.baseScale = height / this.imageEl.OriginHeight;
31731             }
31732             
31733             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31734                 height = this.thumbEl.getWidth();
31735                 this.baseScale = height / this.imageEl.OriginHeight;
31736                 
31737                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31738                     width = this.thumbEl.getHeight();
31739                     this.baseScale = width / this.imageEl.OriginWidth;
31740                 }
31741             }
31742             
31743             return;
31744         }
31745         
31746         width = this.thumbEl.getWidth();
31747         this.baseScale = width / this.imageEl.OriginWidth;
31748         
31749         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31750             height = this.thumbEl.getHeight();
31751             this.baseScale = height / this.imageEl.OriginHeight;
31752         }
31753         
31754         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31755             
31756             height = this.thumbEl.getHeight();
31757             this.baseScale = height / this.imageEl.OriginHeight;
31758             
31759             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31760                 width = this.thumbEl.getWidth();
31761                 this.baseScale = width / this.imageEl.OriginWidth;
31762             }
31763             
31764         }
31765         
31766         return;
31767     },
31768     
31769     getScaleLevel : function()
31770     {
31771         return this.baseScale * Math.pow(1.1, this.scale);
31772     },
31773     
31774     onTouchStart : function(e)
31775     {
31776         if(!this.canvasLoaded){
31777             this.beforeSelectFile(e);
31778             return;
31779         }
31780         
31781         var touches = e.browserEvent.touches;
31782         
31783         if(!touches){
31784             return;
31785         }
31786         
31787         if(touches.length == 1){
31788             this.onMouseDown(e);
31789             return;
31790         }
31791         
31792         if(touches.length != 2){
31793             return;
31794         }
31795         
31796         var coords = [];
31797         
31798         for(var i = 0, finger; finger = touches[i]; i++){
31799             coords.push(finger.pageX, finger.pageY);
31800         }
31801         
31802         var x = Math.pow(coords[0] - coords[2], 2);
31803         var y = Math.pow(coords[1] - coords[3], 2);
31804         
31805         this.startDistance = Math.sqrt(x + y);
31806         
31807         this.startScale = this.scale;
31808         
31809         this.pinching = true;
31810         this.dragable = false;
31811         
31812     },
31813     
31814     onTouchMove : function(e)
31815     {
31816         if(!this.pinching && !this.dragable){
31817             return;
31818         }
31819         
31820         var touches = e.browserEvent.touches;
31821         
31822         if(!touches){
31823             return;
31824         }
31825         
31826         if(this.dragable){
31827             this.onMouseMove(e);
31828             return;
31829         }
31830         
31831         var coords = [];
31832         
31833         for(var i = 0, finger; finger = touches[i]; i++){
31834             coords.push(finger.pageX, finger.pageY);
31835         }
31836         
31837         var x = Math.pow(coords[0] - coords[2], 2);
31838         var y = Math.pow(coords[1] - coords[3], 2);
31839         
31840         this.endDistance = Math.sqrt(x + y);
31841         
31842         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31843         
31844         if(!this.zoomable()){
31845             this.scale = this.startScale;
31846             return;
31847         }
31848         
31849         this.draw();
31850         
31851     },
31852     
31853     onTouchEnd : function(e)
31854     {
31855         this.pinching = false;
31856         this.dragable = false;
31857         
31858     },
31859     
31860     process : function(file, crop)
31861     {
31862         if(this.loadMask){
31863             this.maskEl.mask(this.loadingText);
31864         }
31865         
31866         this.xhr = new XMLHttpRequest();
31867         
31868         file.xhr = this.xhr;
31869
31870         this.xhr.open(this.method, this.url, true);
31871         
31872         var headers = {
31873             "Accept": "application/json",
31874             "Cache-Control": "no-cache",
31875             "X-Requested-With": "XMLHttpRequest"
31876         };
31877         
31878         for (var headerName in headers) {
31879             var headerValue = headers[headerName];
31880             if (headerValue) {
31881                 this.xhr.setRequestHeader(headerName, headerValue);
31882             }
31883         }
31884         
31885         var _this = this;
31886         
31887         this.xhr.onload = function()
31888         {
31889             _this.xhrOnLoad(_this.xhr);
31890         }
31891         
31892         this.xhr.onerror = function()
31893         {
31894             _this.xhrOnError(_this.xhr);
31895         }
31896         
31897         var formData = new FormData();
31898
31899         formData.append('returnHTML', 'NO');
31900         
31901         if(crop){
31902             formData.append('crop', crop);
31903         }
31904         
31905         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31906             formData.append(this.paramName, file, file.name);
31907         }
31908         
31909         if(typeof(file.filename) != 'undefined'){
31910             formData.append('filename', file.filename);
31911         }
31912         
31913         if(typeof(file.mimetype) != 'undefined'){
31914             formData.append('mimetype', file.mimetype);
31915         }
31916         
31917         if(this.fireEvent('arrange', this, formData) != false){
31918             this.xhr.send(formData);
31919         };
31920     },
31921     
31922     xhrOnLoad : function(xhr)
31923     {
31924         if(this.loadMask){
31925             this.maskEl.unmask();
31926         }
31927         
31928         if (xhr.readyState !== 4) {
31929             this.fireEvent('exception', this, xhr);
31930             return;
31931         }
31932
31933         var response = Roo.decode(xhr.responseText);
31934         
31935         if(!response.success){
31936             this.fireEvent('exception', this, xhr);
31937             return;
31938         }
31939         
31940         var response = Roo.decode(xhr.responseText);
31941         
31942         this.fireEvent('upload', this, response);
31943         
31944     },
31945     
31946     xhrOnError : function()
31947     {
31948         if(this.loadMask){
31949             this.maskEl.unmask();
31950         }
31951         
31952         Roo.log('xhr on error');
31953         
31954         var response = Roo.decode(xhr.responseText);
31955           
31956         Roo.log(response);
31957         
31958     },
31959     
31960     prepare : function(file)
31961     {   
31962         if(this.loadMask){
31963             this.maskEl.mask(this.loadingText);
31964         }
31965         
31966         this.file = false;
31967         this.exif = {};
31968         
31969         if(typeof(file) === 'string'){
31970             this.loadCanvas(file);
31971             return;
31972         }
31973         
31974         if(!file || !this.urlAPI){
31975             return;
31976         }
31977         
31978         this.file = file;
31979         this.cropType = file.type;
31980         
31981         var _this = this;
31982         
31983         if(this.fireEvent('prepare', this, this.file) != false){
31984             
31985             var reader = new FileReader();
31986             
31987             reader.onload = function (e) {
31988                 if (e.target.error) {
31989                     Roo.log(e.target.error);
31990                     return;
31991                 }
31992                 
31993                 var buffer = e.target.result,
31994                     dataView = new DataView(buffer),
31995                     offset = 2,
31996                     maxOffset = dataView.byteLength - 4,
31997                     markerBytes,
31998                     markerLength;
31999                 
32000                 if (dataView.getUint16(0) === 0xffd8) {
32001                     while (offset < maxOffset) {
32002                         markerBytes = dataView.getUint16(offset);
32003                         
32004                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
32005                             markerLength = dataView.getUint16(offset + 2) + 2;
32006                             if (offset + markerLength > dataView.byteLength) {
32007                                 Roo.log('Invalid meta data: Invalid segment size.');
32008                                 break;
32009                             }
32010                             
32011                             if(markerBytes == 0xffe1){
32012                                 _this.parseExifData(
32013                                     dataView,
32014                                     offset,
32015                                     markerLength
32016                                 );
32017                             }
32018                             
32019                             offset += markerLength;
32020                             
32021                             continue;
32022                         }
32023                         
32024                         break;
32025                     }
32026                     
32027                 }
32028                 
32029                 var url = _this.urlAPI.createObjectURL(_this.file);
32030                 
32031                 _this.loadCanvas(url);
32032                 
32033                 return;
32034             }
32035             
32036             reader.readAsArrayBuffer(this.file);
32037             
32038         }
32039         
32040     },
32041     
32042     parseExifData : function(dataView, offset, length)
32043     {
32044         var tiffOffset = offset + 10,
32045             littleEndian,
32046             dirOffset;
32047     
32048         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32049             // No Exif data, might be XMP data instead
32050             return;
32051         }
32052         
32053         // Check for the ASCII code for "Exif" (0x45786966):
32054         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32055             // No Exif data, might be XMP data instead
32056             return;
32057         }
32058         if (tiffOffset + 8 > dataView.byteLength) {
32059             Roo.log('Invalid Exif data: Invalid segment size.');
32060             return;
32061         }
32062         // Check for the two null bytes:
32063         if (dataView.getUint16(offset + 8) !== 0x0000) {
32064             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32065             return;
32066         }
32067         // Check the byte alignment:
32068         switch (dataView.getUint16(tiffOffset)) {
32069         case 0x4949:
32070             littleEndian = true;
32071             break;
32072         case 0x4D4D:
32073             littleEndian = false;
32074             break;
32075         default:
32076             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32077             return;
32078         }
32079         // Check for the TIFF tag marker (0x002A):
32080         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32081             Roo.log('Invalid Exif data: Missing TIFF marker.');
32082             return;
32083         }
32084         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32085         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32086         
32087         this.parseExifTags(
32088             dataView,
32089             tiffOffset,
32090             tiffOffset + dirOffset,
32091             littleEndian
32092         );
32093     },
32094     
32095     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32096     {
32097         var tagsNumber,
32098             dirEndOffset,
32099             i;
32100         if (dirOffset + 6 > dataView.byteLength) {
32101             Roo.log('Invalid Exif data: Invalid directory offset.');
32102             return;
32103         }
32104         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32105         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32106         if (dirEndOffset + 4 > dataView.byteLength) {
32107             Roo.log('Invalid Exif data: Invalid directory size.');
32108             return;
32109         }
32110         for (i = 0; i < tagsNumber; i += 1) {
32111             this.parseExifTag(
32112                 dataView,
32113                 tiffOffset,
32114                 dirOffset + 2 + 12 * i, // tag offset
32115                 littleEndian
32116             );
32117         }
32118         // Return the offset to the next directory:
32119         return dataView.getUint32(dirEndOffset, littleEndian);
32120     },
32121     
32122     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32123     {
32124         var tag = dataView.getUint16(offset, littleEndian);
32125         
32126         this.exif[tag] = this.getExifValue(
32127             dataView,
32128             tiffOffset,
32129             offset,
32130             dataView.getUint16(offset + 2, littleEndian), // tag type
32131             dataView.getUint32(offset + 4, littleEndian), // tag length
32132             littleEndian
32133         );
32134     },
32135     
32136     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32137     {
32138         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32139             tagSize,
32140             dataOffset,
32141             values,
32142             i,
32143             str,
32144             c;
32145     
32146         if (!tagType) {
32147             Roo.log('Invalid Exif data: Invalid tag type.');
32148             return;
32149         }
32150         
32151         tagSize = tagType.size * length;
32152         // Determine if the value is contained in the dataOffset bytes,
32153         // or if the value at the dataOffset is a pointer to the actual data:
32154         dataOffset = tagSize > 4 ?
32155                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32156         if (dataOffset + tagSize > dataView.byteLength) {
32157             Roo.log('Invalid Exif data: Invalid data offset.');
32158             return;
32159         }
32160         if (length === 1) {
32161             return tagType.getValue(dataView, dataOffset, littleEndian);
32162         }
32163         values = [];
32164         for (i = 0; i < length; i += 1) {
32165             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32166         }
32167         
32168         if (tagType.ascii) {
32169             str = '';
32170             // Concatenate the chars:
32171             for (i = 0; i < values.length; i += 1) {
32172                 c = values[i];
32173                 // Ignore the terminating NULL byte(s):
32174                 if (c === '\u0000') {
32175                     break;
32176                 }
32177                 str += c;
32178             }
32179             return str;
32180         }
32181         return values;
32182     }
32183     
32184 });
32185
32186 Roo.apply(Roo.bootstrap.UploadCropbox, {
32187     tags : {
32188         'Orientation': 0x0112
32189     },
32190     
32191     Orientation: {
32192             1: 0, //'top-left',
32193 //            2: 'top-right',
32194             3: 180, //'bottom-right',
32195 //            4: 'bottom-left',
32196 //            5: 'left-top',
32197             6: 90, //'right-top',
32198 //            7: 'right-bottom',
32199             8: 270 //'left-bottom'
32200     },
32201     
32202     exifTagTypes : {
32203         // byte, 8-bit unsigned int:
32204         1: {
32205             getValue: function (dataView, dataOffset) {
32206                 return dataView.getUint8(dataOffset);
32207             },
32208             size: 1
32209         },
32210         // ascii, 8-bit byte:
32211         2: {
32212             getValue: function (dataView, dataOffset) {
32213                 return String.fromCharCode(dataView.getUint8(dataOffset));
32214             },
32215             size: 1,
32216             ascii: true
32217         },
32218         // short, 16 bit int:
32219         3: {
32220             getValue: function (dataView, dataOffset, littleEndian) {
32221                 return dataView.getUint16(dataOffset, littleEndian);
32222             },
32223             size: 2
32224         },
32225         // long, 32 bit int:
32226         4: {
32227             getValue: function (dataView, dataOffset, littleEndian) {
32228                 return dataView.getUint32(dataOffset, littleEndian);
32229             },
32230             size: 4
32231         },
32232         // rational = two long values, first is numerator, second is denominator:
32233         5: {
32234             getValue: function (dataView, dataOffset, littleEndian) {
32235                 return dataView.getUint32(dataOffset, littleEndian) /
32236                     dataView.getUint32(dataOffset + 4, littleEndian);
32237             },
32238             size: 8
32239         },
32240         // slong, 32 bit signed int:
32241         9: {
32242             getValue: function (dataView, dataOffset, littleEndian) {
32243                 return dataView.getInt32(dataOffset, littleEndian);
32244             },
32245             size: 4
32246         },
32247         // srational, two slongs, first is numerator, second is denominator:
32248         10: {
32249             getValue: function (dataView, dataOffset, littleEndian) {
32250                 return dataView.getInt32(dataOffset, littleEndian) /
32251                     dataView.getInt32(dataOffset + 4, littleEndian);
32252             },
32253             size: 8
32254         }
32255     },
32256     
32257     footer : {
32258         STANDARD : [
32259             {
32260                 tag : 'div',
32261                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32262                 action : 'rotate-left',
32263                 cn : [
32264                     {
32265                         tag : 'button',
32266                         cls : 'btn btn-default',
32267                         html : '<i class="fa fa-undo"></i>'
32268                     }
32269                 ]
32270             },
32271             {
32272                 tag : 'div',
32273                 cls : 'btn-group roo-upload-cropbox-picture',
32274                 action : 'picture',
32275                 cn : [
32276                     {
32277                         tag : 'button',
32278                         cls : 'btn btn-default',
32279                         html : '<i class="fa fa-picture-o"></i>'
32280                     }
32281                 ]
32282             },
32283             {
32284                 tag : 'div',
32285                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32286                 action : 'rotate-right',
32287                 cn : [
32288                     {
32289                         tag : 'button',
32290                         cls : 'btn btn-default',
32291                         html : '<i class="fa fa-repeat"></i>'
32292                     }
32293                 ]
32294             }
32295         ],
32296         DOCUMENT : [
32297             {
32298                 tag : 'div',
32299                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32300                 action : 'rotate-left',
32301                 cn : [
32302                     {
32303                         tag : 'button',
32304                         cls : 'btn btn-default',
32305                         html : '<i class="fa fa-undo"></i>'
32306                     }
32307                 ]
32308             },
32309             {
32310                 tag : 'div',
32311                 cls : 'btn-group roo-upload-cropbox-download',
32312                 action : 'download',
32313                 cn : [
32314                     {
32315                         tag : 'button',
32316                         cls : 'btn btn-default',
32317                         html : '<i class="fa fa-download"></i>'
32318                     }
32319                 ]
32320             },
32321             {
32322                 tag : 'div',
32323                 cls : 'btn-group roo-upload-cropbox-crop',
32324                 action : 'crop',
32325                 cn : [
32326                     {
32327                         tag : 'button',
32328                         cls : 'btn btn-default',
32329                         html : '<i class="fa fa-crop"></i>'
32330                     }
32331                 ]
32332             },
32333             {
32334                 tag : 'div',
32335                 cls : 'btn-group roo-upload-cropbox-trash',
32336                 action : 'trash',
32337                 cn : [
32338                     {
32339                         tag : 'button',
32340                         cls : 'btn btn-default',
32341                         html : '<i class="fa fa-trash"></i>'
32342                     }
32343                 ]
32344             },
32345             {
32346                 tag : 'div',
32347                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32348                 action : 'rotate-right',
32349                 cn : [
32350                     {
32351                         tag : 'button',
32352                         cls : 'btn btn-default',
32353                         html : '<i class="fa fa-repeat"></i>'
32354                     }
32355                 ]
32356             }
32357         ],
32358         ROTATOR : [
32359             {
32360                 tag : 'div',
32361                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32362                 action : 'rotate-left',
32363                 cn : [
32364                     {
32365                         tag : 'button',
32366                         cls : 'btn btn-default',
32367                         html : '<i class="fa fa-undo"></i>'
32368                     }
32369                 ]
32370             },
32371             {
32372                 tag : 'div',
32373                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32374                 action : 'rotate-right',
32375                 cn : [
32376                     {
32377                         tag : 'button',
32378                         cls : 'btn btn-default',
32379                         html : '<i class="fa fa-repeat"></i>'
32380                     }
32381                 ]
32382             }
32383         ]
32384     }
32385 });
32386
32387 /*
32388 * Licence: LGPL
32389 */
32390
32391 /**
32392  * @class Roo.bootstrap.DocumentManager
32393  * @extends Roo.bootstrap.Component
32394  * Bootstrap DocumentManager class
32395  * @cfg {String} paramName default 'imageUpload'
32396  * @cfg {String} toolTipName default 'filename'
32397  * @cfg {String} method default POST
32398  * @cfg {String} url action url
32399  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32400  * @cfg {Boolean} multiple multiple upload default true
32401  * @cfg {Number} thumbSize default 300
32402  * @cfg {String} fieldLabel
32403  * @cfg {Number} labelWidth default 4
32404  * @cfg {String} labelAlign (left|top) default left
32405  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32406 * @cfg {Number} labellg set the width of label (1-12)
32407  * @cfg {Number} labelmd set the width of label (1-12)
32408  * @cfg {Number} labelsm set the width of label (1-12)
32409  * @cfg {Number} labelxs set the width of label (1-12)
32410  * 
32411  * @constructor
32412  * Create a new DocumentManager
32413  * @param {Object} config The config object
32414  */
32415
32416 Roo.bootstrap.DocumentManager = function(config){
32417     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32418     
32419     this.files = [];
32420     this.delegates = [];
32421     
32422     this.addEvents({
32423         /**
32424          * @event initial
32425          * Fire when initial the DocumentManager
32426          * @param {Roo.bootstrap.DocumentManager} this
32427          */
32428         "initial" : true,
32429         /**
32430          * @event inspect
32431          * inspect selected file
32432          * @param {Roo.bootstrap.DocumentManager} this
32433          * @param {File} file
32434          */
32435         "inspect" : true,
32436         /**
32437          * @event exception
32438          * Fire when xhr load exception
32439          * @param {Roo.bootstrap.DocumentManager} this
32440          * @param {XMLHttpRequest} xhr
32441          */
32442         "exception" : true,
32443         /**
32444          * @event afterupload
32445          * Fire when xhr load exception
32446          * @param {Roo.bootstrap.DocumentManager} this
32447          * @param {XMLHttpRequest} xhr
32448          */
32449         "afterupload" : true,
32450         /**
32451          * @event prepare
32452          * prepare the form data
32453          * @param {Roo.bootstrap.DocumentManager} this
32454          * @param {Object} formData
32455          */
32456         "prepare" : true,
32457         /**
32458          * @event remove
32459          * Fire when remove the file
32460          * @param {Roo.bootstrap.DocumentManager} this
32461          * @param {Object} file
32462          */
32463         "remove" : true,
32464         /**
32465          * @event refresh
32466          * Fire after refresh the file
32467          * @param {Roo.bootstrap.DocumentManager} this
32468          */
32469         "refresh" : true,
32470         /**
32471          * @event click
32472          * Fire after click the image
32473          * @param {Roo.bootstrap.DocumentManager} this
32474          * @param {Object} file
32475          */
32476         "click" : true,
32477         /**
32478          * @event edit
32479          * Fire when upload a image and editable set to true
32480          * @param {Roo.bootstrap.DocumentManager} this
32481          * @param {Object} file
32482          */
32483         "edit" : true,
32484         /**
32485          * @event beforeselectfile
32486          * Fire before select file
32487          * @param {Roo.bootstrap.DocumentManager} this
32488          */
32489         "beforeselectfile" : true,
32490         /**
32491          * @event process
32492          * Fire before process file
32493          * @param {Roo.bootstrap.DocumentManager} this
32494          * @param {Object} file
32495          */
32496         "process" : true,
32497         /**
32498          * @event previewrendered
32499          * Fire when preview rendered
32500          * @param {Roo.bootstrap.DocumentManager} this
32501          * @param {Object} file
32502          */
32503         "previewrendered" : true,
32504         /**
32505          */
32506         "previewResize" : true
32507         
32508     });
32509 };
32510
32511 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32512     
32513     boxes : 0,
32514     inputName : '',
32515     thumbSize : 300,
32516     multiple : true,
32517     files : false,
32518     method : 'POST',
32519     url : '',
32520     paramName : 'imageUpload',
32521     toolTipName : 'filename',
32522     fieldLabel : '',
32523     labelWidth : 4,
32524     labelAlign : 'left',
32525     editable : true,
32526     delegates : false,
32527     xhr : false, 
32528     
32529     labellg : 0,
32530     labelmd : 0,
32531     labelsm : 0,
32532     labelxs : 0,
32533     
32534     getAutoCreate : function()
32535     {   
32536         var managerWidget = {
32537             tag : 'div',
32538             cls : 'roo-document-manager',
32539             cn : [
32540                 {
32541                     tag : 'input',
32542                     cls : 'roo-document-manager-selector',
32543                     type : 'file'
32544                 },
32545                 {
32546                     tag : 'div',
32547                     cls : 'roo-document-manager-uploader',
32548                     cn : [
32549                         {
32550                             tag : 'div',
32551                             cls : 'roo-document-manager-upload-btn',
32552                             html : '<i class="fa fa-plus"></i>'
32553                         }
32554                     ]
32555                     
32556                 }
32557             ]
32558         };
32559         
32560         var content = [
32561             {
32562                 tag : 'div',
32563                 cls : 'column col-md-12',
32564                 cn : managerWidget
32565             }
32566         ];
32567         
32568         if(this.fieldLabel.length){
32569             
32570             content = [
32571                 {
32572                     tag : 'div',
32573                     cls : 'column col-md-12',
32574                     html : this.fieldLabel
32575                 },
32576                 {
32577                     tag : 'div',
32578                     cls : 'column col-md-12',
32579                     cn : managerWidget
32580                 }
32581             ];
32582
32583             if(this.labelAlign == 'left'){
32584                 content = [
32585                     {
32586                         tag : 'div',
32587                         cls : 'column',
32588                         html : this.fieldLabel
32589                     },
32590                     {
32591                         tag : 'div',
32592                         cls : 'column',
32593                         cn : managerWidget
32594                     }
32595                 ];
32596                 
32597                 if(this.labelWidth > 12){
32598                     content[0].style = "width: " + this.labelWidth + 'px';
32599                 }
32600
32601                 if(this.labelWidth < 13 && this.labelmd == 0){
32602                     this.labelmd = this.labelWidth;
32603                 }
32604
32605                 if(this.labellg > 0){
32606                     content[0].cls += ' col-lg-' + this.labellg;
32607                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32608                 }
32609
32610                 if(this.labelmd > 0){
32611                     content[0].cls += ' col-md-' + this.labelmd;
32612                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32613                 }
32614
32615                 if(this.labelsm > 0){
32616                     content[0].cls += ' col-sm-' + this.labelsm;
32617                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32618                 }
32619
32620                 if(this.labelxs > 0){
32621                     content[0].cls += ' col-xs-' + this.labelxs;
32622                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32623                 }
32624                 
32625             }
32626         }
32627         
32628         var cfg = {
32629             tag : 'div',
32630             cls : 'row clearfix',
32631             cn : content
32632         };
32633         
32634         return cfg;
32635         
32636     },
32637     
32638     initEvents : function()
32639     {
32640         this.managerEl = this.el.select('.roo-document-manager', true).first();
32641         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32642         
32643         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32644         this.selectorEl.hide();
32645         
32646         if(this.multiple){
32647             this.selectorEl.attr('multiple', 'multiple');
32648         }
32649         
32650         this.selectorEl.on('change', this.onFileSelected, this);
32651         
32652         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32653         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32654         
32655         this.uploader.on('click', this.onUploaderClick, this);
32656         
32657         this.renderProgressDialog();
32658         
32659         var _this = this;
32660         
32661         window.addEventListener("resize", function() { _this.refresh(); } );
32662         
32663         this.fireEvent('initial', this);
32664     },
32665     
32666     renderProgressDialog : function()
32667     {
32668         var _this = this;
32669         
32670         this.progressDialog = new Roo.bootstrap.Modal({
32671             cls : 'roo-document-manager-progress-dialog',
32672             allow_close : false,
32673             animate : false,
32674             title : '',
32675             buttons : [
32676                 {
32677                     name  :'cancel',
32678                     weight : 'danger',
32679                     html : 'Cancel'
32680                 }
32681             ], 
32682             listeners : { 
32683                 btnclick : function() {
32684                     _this.uploadCancel();
32685                     this.hide();
32686                 }
32687             }
32688         });
32689          
32690         this.progressDialog.render(Roo.get(document.body));
32691          
32692         this.progress = new Roo.bootstrap.Progress({
32693             cls : 'roo-document-manager-progress',
32694             active : true,
32695             striped : true
32696         });
32697         
32698         this.progress.render(this.progressDialog.getChildContainer());
32699         
32700         this.progressBar = new Roo.bootstrap.ProgressBar({
32701             cls : 'roo-document-manager-progress-bar',
32702             aria_valuenow : 0,
32703             aria_valuemin : 0,
32704             aria_valuemax : 12,
32705             panel : 'success'
32706         });
32707         
32708         this.progressBar.render(this.progress.getChildContainer());
32709     },
32710     
32711     onUploaderClick : function(e)
32712     {
32713         e.preventDefault();
32714      
32715         if(this.fireEvent('beforeselectfile', this) != false){
32716             this.selectorEl.dom.click();
32717         }
32718         
32719     },
32720     
32721     onFileSelected : function(e)
32722     {
32723         e.preventDefault();
32724         
32725         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32726             return;
32727         }
32728         
32729         Roo.each(this.selectorEl.dom.files, function(file){
32730             if(this.fireEvent('inspect', this, file) != false){
32731                 this.files.push(file);
32732             }
32733         }, this);
32734         
32735         this.queue();
32736         
32737     },
32738     
32739     queue : function()
32740     {
32741         this.selectorEl.dom.value = '';
32742         
32743         if(!this.files || !this.files.length){
32744             return;
32745         }
32746         
32747         if(this.boxes > 0 && this.files.length > this.boxes){
32748             this.files = this.files.slice(0, this.boxes);
32749         }
32750         
32751         this.uploader.show();
32752         
32753         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32754             this.uploader.hide();
32755         }
32756         
32757         var _this = this;
32758         
32759         var files = [];
32760         
32761         var docs = [];
32762         
32763         Roo.each(this.files, function(file){
32764             
32765             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32766                 var f = this.renderPreview(file);
32767                 files.push(f);
32768                 return;
32769             }
32770             
32771             if(file.type.indexOf('image') != -1){
32772                 this.delegates.push(
32773                     (function(){
32774                         _this.process(file);
32775                     }).createDelegate(this)
32776                 );
32777         
32778                 return;
32779             }
32780             
32781             docs.push(
32782                 (function(){
32783                     _this.process(file);
32784                 }).createDelegate(this)
32785             );
32786             
32787         }, this);
32788         
32789         this.files = files;
32790         
32791         this.delegates = this.delegates.concat(docs);
32792         
32793         if(!this.delegates.length){
32794             this.refresh();
32795             return;
32796         }
32797         
32798         this.progressBar.aria_valuemax = this.delegates.length;
32799         
32800         this.arrange();
32801         
32802         return;
32803     },
32804     
32805     arrange : function()
32806     {
32807         if(!this.delegates.length){
32808             this.progressDialog.hide();
32809             this.refresh();
32810             return;
32811         }
32812         
32813         var delegate = this.delegates.shift();
32814         
32815         this.progressDialog.show();
32816         
32817         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32818         
32819         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32820         
32821         delegate();
32822     },
32823     
32824     refresh : function()
32825     {
32826         this.uploader.show();
32827         
32828         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32829             this.uploader.hide();
32830         }
32831         
32832         Roo.isTouch ? this.closable(false) : this.closable(true);
32833         
32834         this.fireEvent('refresh', this);
32835     },
32836     
32837     onRemove : function(e, el, o)
32838     {
32839         e.preventDefault();
32840         
32841         this.fireEvent('remove', this, o);
32842         
32843     },
32844     
32845     remove : function(o)
32846     {
32847         var files = [];
32848         
32849         Roo.each(this.files, function(file){
32850             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32851                 files.push(file);
32852                 return;
32853             }
32854
32855             o.target.remove();
32856
32857         }, this);
32858         
32859         this.files = files;
32860         
32861         this.refresh();
32862     },
32863     
32864     clear : function()
32865     {
32866         Roo.each(this.files, function(file){
32867             if(!file.target){
32868                 return;
32869             }
32870             
32871             file.target.remove();
32872
32873         }, this);
32874         
32875         this.files = [];
32876         
32877         this.refresh();
32878     },
32879     
32880     onClick : function(e, el, o)
32881     {
32882         e.preventDefault();
32883         
32884         this.fireEvent('click', this, o);
32885         
32886     },
32887     
32888     closable : function(closable)
32889     {
32890         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32891             
32892             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32893             
32894             if(closable){
32895                 el.show();
32896                 return;
32897             }
32898             
32899             el.hide();
32900             
32901         }, this);
32902     },
32903     
32904     xhrOnLoad : function(xhr)
32905     {
32906         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32907             el.remove();
32908         }, this);
32909         
32910         if (xhr.readyState !== 4) {
32911             this.arrange();
32912             this.fireEvent('exception', this, xhr);
32913             return;
32914         }
32915
32916         var response = Roo.decode(xhr.responseText);
32917         
32918         if(!response.success){
32919             this.arrange();
32920             this.fireEvent('exception', this, xhr);
32921             return;
32922         }
32923         
32924         var file = this.renderPreview(response.data);
32925         
32926         this.files.push(file);
32927         
32928         this.arrange();
32929         
32930         this.fireEvent('afterupload', this, xhr);
32931         
32932     },
32933     
32934     xhrOnError : function(xhr)
32935     {
32936         Roo.log('xhr on error');
32937         
32938         var response = Roo.decode(xhr.responseText);
32939           
32940         Roo.log(response);
32941         
32942         this.arrange();
32943     },
32944     
32945     process : function(file)
32946     {
32947         if(this.fireEvent('process', this, file) !== false){
32948             if(this.editable && file.type.indexOf('image') != -1){
32949                 this.fireEvent('edit', this, file);
32950                 return;
32951             }
32952
32953             this.uploadStart(file, false);
32954
32955             return;
32956         }
32957         
32958     },
32959     
32960     uploadStart : function(file, crop)
32961     {
32962         this.xhr = new XMLHttpRequest();
32963         
32964         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32965             this.arrange();
32966             return;
32967         }
32968         
32969         file.xhr = this.xhr;
32970             
32971         this.managerEl.createChild({
32972             tag : 'div',
32973             cls : 'roo-document-manager-loading',
32974             cn : [
32975                 {
32976                     tag : 'div',
32977                     tooltip : file.name,
32978                     cls : 'roo-document-manager-thumb',
32979                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32980                 }
32981             ]
32982
32983         });
32984
32985         this.xhr.open(this.method, this.url, true);
32986         
32987         var headers = {
32988             "Accept": "application/json",
32989             "Cache-Control": "no-cache",
32990             "X-Requested-With": "XMLHttpRequest"
32991         };
32992         
32993         for (var headerName in headers) {
32994             var headerValue = headers[headerName];
32995             if (headerValue) {
32996                 this.xhr.setRequestHeader(headerName, headerValue);
32997             }
32998         }
32999         
33000         var _this = this;
33001         
33002         this.xhr.onload = function()
33003         {
33004             _this.xhrOnLoad(_this.xhr);
33005         }
33006         
33007         this.xhr.onerror = function()
33008         {
33009             _this.xhrOnError(_this.xhr);
33010         }
33011         
33012         var formData = new FormData();
33013
33014         formData.append('returnHTML', 'NO');
33015         
33016         if(crop){
33017             formData.append('crop', crop);
33018         }
33019         
33020         formData.append(this.paramName, file, file.name);
33021         
33022         var options = {
33023             file : file, 
33024             manually : false
33025         };
33026         
33027         if(this.fireEvent('prepare', this, formData, options) != false){
33028             
33029             if(options.manually){
33030                 return;
33031             }
33032             
33033             this.xhr.send(formData);
33034             return;
33035         };
33036         
33037         this.uploadCancel();
33038     },
33039     
33040     uploadCancel : function()
33041     {
33042         if (this.xhr) {
33043             this.xhr.abort();
33044         }
33045         
33046         this.delegates = [];
33047         
33048         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33049             el.remove();
33050         }, this);
33051         
33052         this.arrange();
33053     },
33054     
33055     renderPreview : function(file)
33056     {
33057         if(typeof(file.target) != 'undefined' && file.target){
33058             return file;
33059         }
33060         
33061         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33062         
33063         var previewEl = this.managerEl.createChild({
33064             tag : 'div',
33065             cls : 'roo-document-manager-preview',
33066             cn : [
33067                 {
33068                     tag : 'div',
33069                     tooltip : file[this.toolTipName],
33070                     cls : 'roo-document-manager-thumb',
33071                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33072                 },
33073                 {
33074                     tag : 'button',
33075                     cls : 'close',
33076                     html : '<i class="fa fa-times-circle"></i>'
33077                 }
33078             ]
33079         });
33080
33081         var close = previewEl.select('button.close', true).first();
33082
33083         close.on('click', this.onRemove, this, file);
33084
33085         file.target = previewEl;
33086
33087         var image = previewEl.select('img', true).first();
33088         
33089         var _this = this;
33090         
33091         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33092         
33093         image.on('click', this.onClick, this, file);
33094         
33095         this.fireEvent('previewrendered', this, file);
33096         
33097         return file;
33098         
33099     },
33100     
33101     onPreviewLoad : function(file, image)
33102     {
33103         if(typeof(file.target) == 'undefined' || !file.target){
33104             return;
33105         }
33106         
33107         var width = image.dom.naturalWidth || image.dom.width;
33108         var height = image.dom.naturalHeight || image.dom.height;
33109         
33110         if(!this.previewResize) {
33111             return;
33112         }
33113         
33114         if(width > height){
33115             file.target.addClass('wide');
33116             return;
33117         }
33118         
33119         file.target.addClass('tall');
33120         return;
33121         
33122     },
33123     
33124     uploadFromSource : function(file, crop)
33125     {
33126         this.xhr = new XMLHttpRequest();
33127         
33128         this.managerEl.createChild({
33129             tag : 'div',
33130             cls : 'roo-document-manager-loading',
33131             cn : [
33132                 {
33133                     tag : 'div',
33134                     tooltip : file.name,
33135                     cls : 'roo-document-manager-thumb',
33136                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33137                 }
33138             ]
33139
33140         });
33141
33142         this.xhr.open(this.method, this.url, true);
33143         
33144         var headers = {
33145             "Accept": "application/json",
33146             "Cache-Control": "no-cache",
33147             "X-Requested-With": "XMLHttpRequest"
33148         };
33149         
33150         for (var headerName in headers) {
33151             var headerValue = headers[headerName];
33152             if (headerValue) {
33153                 this.xhr.setRequestHeader(headerName, headerValue);
33154             }
33155         }
33156         
33157         var _this = this;
33158         
33159         this.xhr.onload = function()
33160         {
33161             _this.xhrOnLoad(_this.xhr);
33162         }
33163         
33164         this.xhr.onerror = function()
33165         {
33166             _this.xhrOnError(_this.xhr);
33167         }
33168         
33169         var formData = new FormData();
33170
33171         formData.append('returnHTML', 'NO');
33172         
33173         formData.append('crop', crop);
33174         
33175         if(typeof(file.filename) != 'undefined'){
33176             formData.append('filename', file.filename);
33177         }
33178         
33179         if(typeof(file.mimetype) != 'undefined'){
33180             formData.append('mimetype', file.mimetype);
33181         }
33182         
33183         Roo.log(formData);
33184         
33185         if(this.fireEvent('prepare', this, formData) != false){
33186             this.xhr.send(formData);
33187         };
33188     }
33189 });
33190
33191 /*
33192 * Licence: LGPL
33193 */
33194
33195 /**
33196  * @class Roo.bootstrap.DocumentViewer
33197  * @extends Roo.bootstrap.Component
33198  * Bootstrap DocumentViewer class
33199  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33200  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33201  * 
33202  * @constructor
33203  * Create a new DocumentViewer
33204  * @param {Object} config The config object
33205  */
33206
33207 Roo.bootstrap.DocumentViewer = function(config){
33208     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33209     
33210     this.addEvents({
33211         /**
33212          * @event initial
33213          * Fire after initEvent
33214          * @param {Roo.bootstrap.DocumentViewer} this
33215          */
33216         "initial" : true,
33217         /**
33218          * @event click
33219          * Fire after click
33220          * @param {Roo.bootstrap.DocumentViewer} this
33221          */
33222         "click" : true,
33223         /**
33224          * @event download
33225          * Fire after download button
33226          * @param {Roo.bootstrap.DocumentViewer} this
33227          */
33228         "download" : true,
33229         /**
33230          * @event trash
33231          * Fire after trash button
33232          * @param {Roo.bootstrap.DocumentViewer} this
33233          */
33234         "trash" : true
33235         
33236     });
33237 };
33238
33239 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33240     
33241     showDownload : true,
33242     
33243     showTrash : true,
33244     
33245     getAutoCreate : function()
33246     {
33247         var cfg = {
33248             tag : 'div',
33249             cls : 'roo-document-viewer',
33250             cn : [
33251                 {
33252                     tag : 'div',
33253                     cls : 'roo-document-viewer-body',
33254                     cn : [
33255                         {
33256                             tag : 'div',
33257                             cls : 'roo-document-viewer-thumb',
33258                             cn : [
33259                                 {
33260                                     tag : 'img',
33261                                     cls : 'roo-document-viewer-image'
33262                                 }
33263                             ]
33264                         }
33265                     ]
33266                 },
33267                 {
33268                     tag : 'div',
33269                     cls : 'roo-document-viewer-footer',
33270                     cn : {
33271                         tag : 'div',
33272                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33273                         cn : [
33274                             {
33275                                 tag : 'div',
33276                                 cls : 'btn-group roo-document-viewer-download',
33277                                 cn : [
33278                                     {
33279                                         tag : 'button',
33280                                         cls : 'btn btn-default',
33281                                         html : '<i class="fa fa-download"></i>'
33282                                     }
33283                                 ]
33284                             },
33285                             {
33286                                 tag : 'div',
33287                                 cls : 'btn-group roo-document-viewer-trash',
33288                                 cn : [
33289                                     {
33290                                         tag : 'button',
33291                                         cls : 'btn btn-default',
33292                                         html : '<i class="fa fa-trash"></i>'
33293                                     }
33294                                 ]
33295                             }
33296                         ]
33297                     }
33298                 }
33299             ]
33300         };
33301         
33302         return cfg;
33303     },
33304     
33305     initEvents : function()
33306     {
33307         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33308         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33309         
33310         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33311         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33312         
33313         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33314         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33315         
33316         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33317         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33318         
33319         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33320         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33321         
33322         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33323         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33324         
33325         this.bodyEl.on('click', this.onClick, this);
33326         this.downloadBtn.on('click', this.onDownload, this);
33327         this.trashBtn.on('click', this.onTrash, this);
33328         
33329         this.downloadBtn.hide();
33330         this.trashBtn.hide();
33331         
33332         if(this.showDownload){
33333             this.downloadBtn.show();
33334         }
33335         
33336         if(this.showTrash){
33337             this.trashBtn.show();
33338         }
33339         
33340         if(!this.showDownload && !this.showTrash) {
33341             this.footerEl.hide();
33342         }
33343         
33344     },
33345     
33346     initial : function()
33347     {
33348         this.fireEvent('initial', this);
33349         
33350     },
33351     
33352     onClick : function(e)
33353     {
33354         e.preventDefault();
33355         
33356         this.fireEvent('click', this);
33357     },
33358     
33359     onDownload : function(e)
33360     {
33361         e.preventDefault();
33362         
33363         this.fireEvent('download', this);
33364     },
33365     
33366     onTrash : function(e)
33367     {
33368         e.preventDefault();
33369         
33370         this.fireEvent('trash', this);
33371     }
33372     
33373 });
33374 /*
33375  * - LGPL
33376  *
33377  * FieldLabel
33378  * 
33379  */
33380
33381 /**
33382  * @class Roo.bootstrap.form.FieldLabel
33383  * @extends Roo.bootstrap.Component
33384  * Bootstrap FieldLabel class
33385  * @cfg {String} html contents of the element
33386  * @cfg {String} tag tag of the element default label
33387  * @cfg {String} cls class of the element
33388  * @cfg {String} target label target 
33389  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33390  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33391  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33392  * @cfg {String} iconTooltip default "This field is required"
33393  * @cfg {String} indicatorpos (left|right) default left
33394  * 
33395  * @constructor
33396  * Create a new FieldLabel
33397  * @param {Object} config The config object
33398  */
33399
33400 Roo.bootstrap.form.FieldLabel = function(config){
33401     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33402     
33403     this.addEvents({
33404             /**
33405              * @event invalid
33406              * Fires after the field has been marked as invalid.
33407              * @param {Roo.form.FieldLabel} this
33408              * @param {String} msg The validation message
33409              */
33410             invalid : true,
33411             /**
33412              * @event valid
33413              * Fires after the field has been validated with no errors.
33414              * @param {Roo.form.FieldLabel} this
33415              */
33416             valid : true
33417         });
33418 };
33419
33420 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
33421     
33422     tag: 'label',
33423     cls: '',
33424     html: '',
33425     target: '',
33426     allowBlank : true,
33427     invalidClass : 'has-warning',
33428     validClass : 'has-success',
33429     iconTooltip : 'This field is required',
33430     indicatorpos : 'left',
33431     
33432     getAutoCreate : function(){
33433         
33434         var cls = "";
33435         if (!this.allowBlank) {
33436             cls  = "visible";
33437         }
33438         
33439         var cfg = {
33440             tag : this.tag,
33441             cls : 'roo-bootstrap-field-label ' + this.cls,
33442             for : this.target,
33443             cn : [
33444                 {
33445                     tag : 'i',
33446                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33447                     tooltip : this.iconTooltip
33448                 },
33449                 {
33450                     tag : 'span',
33451                     html : this.html
33452                 }
33453             ] 
33454         };
33455         
33456         if(this.indicatorpos == 'right'){
33457             var cfg = {
33458                 tag : this.tag,
33459                 cls : 'roo-bootstrap-field-label ' + this.cls,
33460                 for : this.target,
33461                 cn : [
33462                     {
33463                         tag : 'span',
33464                         html : this.html
33465                     },
33466                     {
33467                         tag : 'i',
33468                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33469                         tooltip : this.iconTooltip
33470                     }
33471                 ] 
33472             };
33473         }
33474         
33475         return cfg;
33476     },
33477     
33478     initEvents: function() 
33479     {
33480         Roo.bootstrap.Element.superclass.initEvents.call(this);
33481         
33482         this.indicator = this.indicatorEl();
33483         
33484         if(this.indicator){
33485             this.indicator.removeClass('visible');
33486             this.indicator.addClass('invisible');
33487         }
33488         
33489         Roo.bootstrap.form.FieldLabel.register(this);
33490     },
33491     
33492     indicatorEl : function()
33493     {
33494         var indicator = this.el.select('i.roo-required-indicator',true).first();
33495         
33496         if(!indicator){
33497             return false;
33498         }
33499         
33500         return indicator;
33501         
33502     },
33503     
33504     /**
33505      * Mark this field as valid
33506      */
33507     markValid : function()
33508     {
33509         if(this.indicator){
33510             this.indicator.removeClass('visible');
33511             this.indicator.addClass('invisible');
33512         }
33513         if (Roo.bootstrap.version == 3) {
33514             this.el.removeClass(this.invalidClass);
33515             this.el.addClass(this.validClass);
33516         } else {
33517             this.el.removeClass('is-invalid');
33518             this.el.addClass('is-valid');
33519         }
33520         
33521         
33522         this.fireEvent('valid', this);
33523     },
33524     
33525     /**
33526      * Mark this field as invalid
33527      * @param {String} msg The validation message
33528      */
33529     markInvalid : function(msg)
33530     {
33531         if(this.indicator){
33532             this.indicator.removeClass('invisible');
33533             this.indicator.addClass('visible');
33534         }
33535           if (Roo.bootstrap.version == 3) {
33536             this.el.removeClass(this.validClass);
33537             this.el.addClass(this.invalidClass);
33538         } else {
33539             this.el.removeClass('is-valid');
33540             this.el.addClass('is-invalid');
33541         }
33542         
33543         
33544         this.fireEvent('invalid', this, msg);
33545     }
33546     
33547    
33548 });
33549
33550 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33551     
33552     groups: {},
33553     
33554      /**
33555     * register a FieldLabel Group
33556     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33557     */
33558     register : function(label)
33559     {
33560         if(this.groups.hasOwnProperty(label.target)){
33561             return;
33562         }
33563      
33564         this.groups[label.target] = label;
33565         
33566     },
33567     /**
33568     * fetch a FieldLabel Group based on the target
33569     * @param {string} target
33570     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33571     */
33572     get: function(target) {
33573         if (typeof(this.groups[target]) == 'undefined') {
33574             return false;
33575         }
33576         
33577         return this.groups[target] ;
33578     }
33579 });
33580
33581  
33582
33583  /*
33584  * - LGPL
33585  *
33586  * page DateSplitField.
33587  * 
33588  */
33589
33590
33591 /**
33592  * @class Roo.bootstrap.form.DateSplitField
33593  * @extends Roo.bootstrap.Component
33594  * Bootstrap DateSplitField class
33595  * @cfg {string} fieldLabel - the label associated
33596  * @cfg {Number} labelWidth set the width of label (0-12)
33597  * @cfg {String} labelAlign (top|left)
33598  * @cfg {Boolean} dayAllowBlank (true|false) default false
33599  * @cfg {Boolean} monthAllowBlank (true|false) default false
33600  * @cfg {Boolean} yearAllowBlank (true|false) default false
33601  * @cfg {string} dayPlaceholder 
33602  * @cfg {string} monthPlaceholder
33603  * @cfg {string} yearPlaceholder
33604  * @cfg {string} dayFormat default 'd'
33605  * @cfg {string} monthFormat default 'm'
33606  * @cfg {string} yearFormat default 'Y'
33607  * @cfg {Number} labellg set the width of label (1-12)
33608  * @cfg {Number} labelmd set the width of label (1-12)
33609  * @cfg {Number} labelsm set the width of label (1-12)
33610  * @cfg {Number} labelxs set the width of label (1-12)
33611
33612  *     
33613  * @constructor
33614  * Create a new DateSplitField
33615  * @param {Object} config The config object
33616  */
33617
33618 Roo.bootstrap.form.DateSplitField = function(config){
33619     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33620     
33621     this.addEvents({
33622         // raw events
33623          /**
33624          * @event years
33625          * getting the data of years
33626          * @param {Roo.bootstrap.form.DateSplitField} this
33627          * @param {Object} years
33628          */
33629         "years" : true,
33630         /**
33631          * @event days
33632          * getting the data of days
33633          * @param {Roo.bootstrap.form.DateSplitField} this
33634          * @param {Object} days
33635          */
33636         "days" : true,
33637         /**
33638          * @event invalid
33639          * Fires after the field has been marked as invalid.
33640          * @param {Roo.form.Field} this
33641          * @param {String} msg The validation message
33642          */
33643         invalid : true,
33644        /**
33645          * @event valid
33646          * Fires after the field has been validated with no errors.
33647          * @param {Roo.form.Field} this
33648          */
33649         valid : true
33650     });
33651 };
33652
33653 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
33654     
33655     fieldLabel : '',
33656     labelAlign : 'top',
33657     labelWidth : 3,
33658     dayAllowBlank : false,
33659     monthAllowBlank : false,
33660     yearAllowBlank : false,
33661     dayPlaceholder : '',
33662     monthPlaceholder : '',
33663     yearPlaceholder : '',
33664     dayFormat : 'd',
33665     monthFormat : 'm',
33666     yearFormat : 'Y',
33667     isFormField : true,
33668     labellg : 0,
33669     labelmd : 0,
33670     labelsm : 0,
33671     labelxs : 0,
33672     
33673     getAutoCreate : function()
33674     {
33675         var cfg = {
33676             tag : 'div',
33677             cls : 'row roo-date-split-field-group',
33678             cn : [
33679                 {
33680                     tag : 'input',
33681                     type : 'hidden',
33682                     cls : 'form-hidden-field roo-date-split-field-group-value',
33683                     name : this.name
33684                 }
33685             ]
33686         };
33687         
33688         var labelCls = 'col-md-12';
33689         var contentCls = 'col-md-4';
33690         
33691         if(this.fieldLabel){
33692             
33693             var label = {
33694                 tag : 'div',
33695                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33696                 cn : [
33697                     {
33698                         tag : 'label',
33699                         html : this.fieldLabel
33700                     }
33701                 ]
33702             };
33703             
33704             if(this.labelAlign == 'left'){
33705             
33706                 if(this.labelWidth > 12){
33707                     label.style = "width: " + this.labelWidth + 'px';
33708                 }
33709
33710                 if(this.labelWidth < 13 && this.labelmd == 0){
33711                     this.labelmd = this.labelWidth;
33712                 }
33713
33714                 if(this.labellg > 0){
33715                     labelCls = ' col-lg-' + this.labellg;
33716                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33717                 }
33718
33719                 if(this.labelmd > 0){
33720                     labelCls = ' col-md-' + this.labelmd;
33721                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33722                 }
33723
33724                 if(this.labelsm > 0){
33725                     labelCls = ' col-sm-' + this.labelsm;
33726                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33727                 }
33728
33729                 if(this.labelxs > 0){
33730                     labelCls = ' col-xs-' + this.labelxs;
33731                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33732                 }
33733             }
33734             
33735             label.cls += ' ' + labelCls;
33736             
33737             cfg.cn.push(label);
33738         }
33739         
33740         Roo.each(['day', 'month', 'year'], function(t){
33741             cfg.cn.push({
33742                 tag : 'div',
33743                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33744             });
33745         }, this);
33746         
33747         return cfg;
33748     },
33749     
33750     inputEl: function ()
33751     {
33752         return this.el.select('.roo-date-split-field-group-value', true).first();
33753     },
33754     
33755     onRender : function(ct, position) 
33756     {
33757         var _this = this;
33758         
33759         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33760         
33761         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33762         
33763         this.dayField = new Roo.bootstrap.form.ComboBox({
33764             allowBlank : this.dayAllowBlank,
33765             alwaysQuery : true,
33766             displayField : 'value',
33767             editable : false,
33768             fieldLabel : '',
33769             forceSelection : true,
33770             mode : 'local',
33771             placeholder : this.dayPlaceholder,
33772             selectOnFocus : true,
33773             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33774             triggerAction : 'all',
33775             typeAhead : true,
33776             valueField : 'value',
33777             store : new Roo.data.SimpleStore({
33778                 data : (function() {    
33779                     var days = [];
33780                     _this.fireEvent('days', _this, days);
33781                     return days;
33782                 })(),
33783                 fields : [ 'value' ]
33784             }),
33785             listeners : {
33786                 select : function (_self, record, index)
33787                 {
33788                     _this.setValue(_this.getValue());
33789                 }
33790             }
33791         });
33792
33793         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33794         
33795         this.monthField = new Roo.bootstrap.form.MonthField({
33796             after : '<i class=\"fa fa-calendar\"></i>',
33797             allowBlank : this.monthAllowBlank,
33798             placeholder : this.monthPlaceholder,
33799             readOnly : true,
33800             listeners : {
33801                 render : function (_self)
33802                 {
33803                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33804                         e.preventDefault();
33805                         _self.focus();
33806                     });
33807                 },
33808                 select : function (_self, oldvalue, newvalue)
33809                 {
33810                     _this.setValue(_this.getValue());
33811                 }
33812             }
33813         });
33814         
33815         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33816         
33817         this.yearField = new Roo.bootstrap.form.ComboBox({
33818             allowBlank : this.yearAllowBlank,
33819             alwaysQuery : true,
33820             displayField : 'value',
33821             editable : false,
33822             fieldLabel : '',
33823             forceSelection : true,
33824             mode : 'local',
33825             placeholder : this.yearPlaceholder,
33826             selectOnFocus : true,
33827             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33828             triggerAction : 'all',
33829             typeAhead : true,
33830             valueField : 'value',
33831             store : new Roo.data.SimpleStore({
33832                 data : (function() {
33833                     var years = [];
33834                     _this.fireEvent('years', _this, years);
33835                     return years;
33836                 })(),
33837                 fields : [ 'value' ]
33838             }),
33839             listeners : {
33840                 select : function (_self, record, index)
33841                 {
33842                     _this.setValue(_this.getValue());
33843                 }
33844             }
33845         });
33846
33847         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33848     },
33849     
33850     setValue : function(v, format)
33851     {
33852         this.inputEl.dom.value = v;
33853         
33854         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33855         
33856         var d = Date.parseDate(v, f);
33857         
33858         if(!d){
33859             this.validate();
33860             return;
33861         }
33862         
33863         this.setDay(d.format(this.dayFormat));
33864         this.setMonth(d.format(this.monthFormat));
33865         this.setYear(d.format(this.yearFormat));
33866         
33867         this.validate();
33868         
33869         return;
33870     },
33871     
33872     setDay : function(v)
33873     {
33874         this.dayField.setValue(v);
33875         this.inputEl.dom.value = this.getValue();
33876         this.validate();
33877         return;
33878     },
33879     
33880     setMonth : function(v)
33881     {
33882         this.monthField.setValue(v, true);
33883         this.inputEl.dom.value = this.getValue();
33884         this.validate();
33885         return;
33886     },
33887     
33888     setYear : function(v)
33889     {
33890         this.yearField.setValue(v);
33891         this.inputEl.dom.value = this.getValue();
33892         this.validate();
33893         return;
33894     },
33895     
33896     getDay : function()
33897     {
33898         return this.dayField.getValue();
33899     },
33900     
33901     getMonth : function()
33902     {
33903         return this.monthField.getValue();
33904     },
33905     
33906     getYear : function()
33907     {
33908         return this.yearField.getValue();
33909     },
33910     
33911     getValue : function()
33912     {
33913         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33914         
33915         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33916         
33917         return date;
33918     },
33919     
33920     reset : function()
33921     {
33922         this.setDay('');
33923         this.setMonth('');
33924         this.setYear('');
33925         this.inputEl.dom.value = '';
33926         this.validate();
33927         return;
33928     },
33929     
33930     validate : function()
33931     {
33932         var d = this.dayField.validate();
33933         var m = this.monthField.validate();
33934         var y = this.yearField.validate();
33935         
33936         var valid = true;
33937         
33938         if(
33939                 (!this.dayAllowBlank && !d) ||
33940                 (!this.monthAllowBlank && !m) ||
33941                 (!this.yearAllowBlank && !y)
33942         ){
33943             valid = false;
33944         }
33945         
33946         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33947             return valid;
33948         }
33949         
33950         if(valid){
33951             this.markValid();
33952             return valid;
33953         }
33954         
33955         this.markInvalid();
33956         
33957         return valid;
33958     },
33959     
33960     markValid : function()
33961     {
33962         
33963         var label = this.el.select('label', true).first();
33964         var icon = this.el.select('i.fa-star', true).first();
33965
33966         if(label && icon){
33967             icon.remove();
33968         }
33969         
33970         this.fireEvent('valid', this);
33971     },
33972     
33973      /**
33974      * Mark this field as invalid
33975      * @param {String} msg The validation message
33976      */
33977     markInvalid : function(msg)
33978     {
33979         
33980         var label = this.el.select('label', true).first();
33981         var icon = this.el.select('i.fa-star', true).first();
33982
33983         if(label && !icon){
33984             this.el.select('.roo-date-split-field-label', true).createChild({
33985                 tag : 'i',
33986                 cls : 'text-danger fa fa-lg fa-star',
33987                 tooltip : 'This field is required',
33988                 style : 'margin-right:5px;'
33989             }, label, true);
33990         }
33991         
33992         this.fireEvent('invalid', this, msg);
33993     },
33994     
33995     clearInvalid : function()
33996     {
33997         var label = this.el.select('label', true).first();
33998         var icon = this.el.select('i.fa-star', true).first();
33999
34000         if(label && icon){
34001             icon.remove();
34002         }
34003         
34004         this.fireEvent('valid', this);
34005     },
34006     
34007     getName: function()
34008     {
34009         return this.name;
34010     }
34011     
34012 });
34013
34014  
34015
34016 /**
34017  * @class Roo.bootstrap.LayoutMasonry
34018  * @extends Roo.bootstrap.Component
34019  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34020  * Bootstrap Layout Masonry class
34021  *
34022  * This is based on 
34023  * http://masonry.desandro.com
34024  *
34025  * The idea is to render all the bricks based on vertical width...
34026  *
34027  * The original code extends 'outlayer' - we might need to use that....
34028
34029  * @constructor
34030  * Create a new Element
34031  * @param {Object} config The config object
34032  */
34033
34034 Roo.bootstrap.LayoutMasonry = function(config){
34035     
34036     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34037     
34038     this.bricks = [];
34039     
34040     Roo.bootstrap.LayoutMasonry.register(this);
34041     
34042     this.addEvents({
34043         // raw events
34044         /**
34045          * @event layout
34046          * Fire after layout the items
34047          * @param {Roo.bootstrap.LayoutMasonry} this
34048          * @param {Roo.EventObject} e
34049          */
34050         "layout" : true
34051     });
34052     
34053 };
34054
34055 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34056     
34057     /**
34058      * @cfg {Boolean} isLayoutInstant = no animation?
34059      */   
34060     isLayoutInstant : false, // needed?
34061    
34062     /**
34063      * @cfg {Number} boxWidth  width of the columns
34064      */   
34065     boxWidth : 450,
34066     
34067       /**
34068      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34069      */   
34070     boxHeight : 0,
34071     
34072     /**
34073      * @cfg {Number} padWidth padding below box..
34074      */   
34075     padWidth : 10, 
34076     
34077     /**
34078      * @cfg {Number} gutter gutter width..
34079      */   
34080     gutter : 10,
34081     
34082      /**
34083      * @cfg {Number} maxCols maximum number of columns
34084      */   
34085     
34086     maxCols: 0,
34087     
34088     /**
34089      * @cfg {Boolean} isAutoInitial defalut true
34090      */   
34091     isAutoInitial : true, 
34092     
34093     containerWidth: 0,
34094     
34095     /**
34096      * @cfg {Boolean} isHorizontal defalut false
34097      */   
34098     isHorizontal : false, 
34099
34100     currentSize : null,
34101     
34102     tag: 'div',
34103     
34104     cls: '',
34105     
34106     bricks: null, //CompositeElement
34107     
34108     cols : 1,
34109     
34110     _isLayoutInited : false,
34111     
34112 //    isAlternative : false, // only use for vertical layout...
34113     
34114     /**
34115      * @cfg {Number} alternativePadWidth padding below box..
34116      */   
34117     alternativePadWidth : 50,
34118     
34119     selectedBrick : [],
34120     
34121     getAutoCreate : function(){
34122         
34123         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34124         
34125         var cfg = {
34126             tag: this.tag,
34127             cls: 'blog-masonary-wrapper ' + this.cls,
34128             cn : {
34129                 cls : 'mas-boxes masonary'
34130             }
34131         };
34132         
34133         return cfg;
34134     },
34135     
34136     getChildContainer: function( )
34137     {
34138         if (this.boxesEl) {
34139             return this.boxesEl;
34140         }
34141         
34142         this.boxesEl = this.el.select('.mas-boxes').first();
34143         
34144         return this.boxesEl;
34145     },
34146     
34147     
34148     initEvents : function()
34149     {
34150         var _this = this;
34151         
34152         if(this.isAutoInitial){
34153             Roo.log('hook children rendered');
34154             this.on('childrenrendered', function() {
34155                 Roo.log('children rendered');
34156                 _this.initial();
34157             } ,this);
34158         }
34159     },
34160     
34161     initial : function()
34162     {
34163         this.selectedBrick = [];
34164         
34165         this.currentSize = this.el.getBox(true);
34166         
34167         Roo.EventManager.onWindowResize(this.resize, this); 
34168
34169         if(!this.isAutoInitial){
34170             this.layout();
34171             return;
34172         }
34173         
34174         this.layout();
34175         
34176         return;
34177         //this.layout.defer(500,this);
34178         
34179     },
34180     
34181     resize : function()
34182     {
34183         var cs = this.el.getBox(true);
34184         
34185         if (
34186                 this.currentSize.width == cs.width && 
34187                 this.currentSize.x == cs.x && 
34188                 this.currentSize.height == cs.height && 
34189                 this.currentSize.y == cs.y 
34190         ) {
34191             Roo.log("no change in with or X or Y");
34192             return;
34193         }
34194         
34195         this.currentSize = cs;
34196         
34197         this.layout();
34198         
34199     },
34200     
34201     layout : function()
34202     {   
34203         this._resetLayout();
34204         
34205         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34206         
34207         this.layoutItems( isInstant );
34208       
34209         this._isLayoutInited = true;
34210         
34211         this.fireEvent('layout', this);
34212         
34213     },
34214     
34215     _resetLayout : function()
34216     {
34217         if(this.isHorizontal){
34218             this.horizontalMeasureColumns();
34219             return;
34220         }
34221         
34222         this.verticalMeasureColumns();
34223         
34224     },
34225     
34226     verticalMeasureColumns : function()
34227     {
34228         this.getContainerWidth();
34229         
34230 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34231 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34232 //            return;
34233 //        }
34234         
34235         var boxWidth = this.boxWidth + this.padWidth;
34236         
34237         if(this.containerWidth < this.boxWidth){
34238             boxWidth = this.containerWidth
34239         }
34240         
34241         var containerWidth = this.containerWidth;
34242         
34243         var cols = Math.floor(containerWidth / boxWidth);
34244         
34245         this.cols = Math.max( cols, 1 );
34246         
34247         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34248         
34249         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34250         
34251         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34252         
34253         this.colWidth = boxWidth + avail - this.padWidth;
34254         
34255         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34256         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34257     },
34258     
34259     horizontalMeasureColumns : function()
34260     {
34261         this.getContainerWidth();
34262         
34263         var boxWidth = this.boxWidth;
34264         
34265         if(this.containerWidth < boxWidth){
34266             boxWidth = this.containerWidth;
34267         }
34268         
34269         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34270         
34271         this.el.setHeight(boxWidth);
34272         
34273     },
34274     
34275     getContainerWidth : function()
34276     {
34277         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34278     },
34279     
34280     layoutItems : function( isInstant )
34281     {
34282         Roo.log(this.bricks);
34283         
34284         var items = Roo.apply([], this.bricks);
34285         
34286         if(this.isHorizontal){
34287             this._horizontalLayoutItems( items , isInstant );
34288             return;
34289         }
34290         
34291 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34292 //            this._verticalAlternativeLayoutItems( items , isInstant );
34293 //            return;
34294 //        }
34295         
34296         this._verticalLayoutItems( items , isInstant );
34297         
34298     },
34299     
34300     _verticalLayoutItems : function ( items , isInstant)
34301     {
34302         if ( !items || !items.length ) {
34303             return;
34304         }
34305         
34306         var standard = [
34307             ['xs', 'xs', 'xs', 'tall'],
34308             ['xs', 'xs', 'tall'],
34309             ['xs', 'xs', 'sm'],
34310             ['xs', 'xs', 'xs'],
34311             ['xs', 'tall'],
34312             ['xs', 'sm'],
34313             ['xs', 'xs'],
34314             ['xs'],
34315             
34316             ['sm', 'xs', 'xs'],
34317             ['sm', 'xs'],
34318             ['sm'],
34319             
34320             ['tall', 'xs', 'xs', 'xs'],
34321             ['tall', 'xs', 'xs'],
34322             ['tall', 'xs'],
34323             ['tall']
34324             
34325         ];
34326         
34327         var queue = [];
34328         
34329         var boxes = [];
34330         
34331         var box = [];
34332         
34333         Roo.each(items, function(item, k){
34334             
34335             switch (item.size) {
34336                 // these layouts take up a full box,
34337                 case 'md' :
34338                 case 'md-left' :
34339                 case 'md-right' :
34340                 case 'wide' :
34341                     
34342                     if(box.length){
34343                         boxes.push(box);
34344                         box = [];
34345                     }
34346                     
34347                     boxes.push([item]);
34348                     
34349                     break;
34350                     
34351                 case 'xs' :
34352                 case 'sm' :
34353                 case 'tall' :
34354                     
34355                     box.push(item);
34356                     
34357                     break;
34358                 default :
34359                     break;
34360                     
34361             }
34362             
34363         }, this);
34364         
34365         if(box.length){
34366             boxes.push(box);
34367             box = [];
34368         }
34369         
34370         var filterPattern = function(box, length)
34371         {
34372             if(!box.length){
34373                 return;
34374             }
34375             
34376             var match = false;
34377             
34378             var pattern = box.slice(0, length);
34379             
34380             var format = [];
34381             
34382             Roo.each(pattern, function(i){
34383                 format.push(i.size);
34384             }, this);
34385             
34386             Roo.each(standard, function(s){
34387                 
34388                 if(String(s) != String(format)){
34389                     return;
34390                 }
34391                 
34392                 match = true;
34393                 return false;
34394                 
34395             }, this);
34396             
34397             if(!match && length == 1){
34398                 return;
34399             }
34400             
34401             if(!match){
34402                 filterPattern(box, length - 1);
34403                 return;
34404             }
34405                 
34406             queue.push(pattern);
34407
34408             box = box.slice(length, box.length);
34409
34410             filterPattern(box, 4);
34411
34412             return;
34413             
34414         }
34415         
34416         Roo.each(boxes, function(box, k){
34417             
34418             if(!box.length){
34419                 return;
34420             }
34421             
34422             if(box.length == 1){
34423                 queue.push(box);
34424                 return;
34425             }
34426             
34427             filterPattern(box, 4);
34428             
34429         }, this);
34430         
34431         this._processVerticalLayoutQueue( queue, isInstant );
34432         
34433     },
34434     
34435 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34436 //    {
34437 //        if ( !items || !items.length ) {
34438 //            return;
34439 //        }
34440 //
34441 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34442 //        
34443 //    },
34444     
34445     _horizontalLayoutItems : function ( items , isInstant)
34446     {
34447         if ( !items || !items.length || items.length < 3) {
34448             return;
34449         }
34450         
34451         items.reverse();
34452         
34453         var eItems = items.slice(0, 3);
34454         
34455         items = items.slice(3, items.length);
34456         
34457         var standard = [
34458             ['xs', 'xs', 'xs', 'wide'],
34459             ['xs', 'xs', 'wide'],
34460             ['xs', 'xs', 'sm'],
34461             ['xs', 'xs', 'xs'],
34462             ['xs', 'wide'],
34463             ['xs', 'sm'],
34464             ['xs', 'xs'],
34465             ['xs'],
34466             
34467             ['sm', 'xs', 'xs'],
34468             ['sm', 'xs'],
34469             ['sm'],
34470             
34471             ['wide', 'xs', 'xs', 'xs'],
34472             ['wide', 'xs', 'xs'],
34473             ['wide', 'xs'],
34474             ['wide'],
34475             
34476             ['wide-thin']
34477         ];
34478         
34479         var queue = [];
34480         
34481         var boxes = [];
34482         
34483         var box = [];
34484         
34485         Roo.each(items, function(item, k){
34486             
34487             switch (item.size) {
34488                 case 'md' :
34489                 case 'md-left' :
34490                 case 'md-right' :
34491                 case 'tall' :
34492                     
34493                     if(box.length){
34494                         boxes.push(box);
34495                         box = [];
34496                     }
34497                     
34498                     boxes.push([item]);
34499                     
34500                     break;
34501                     
34502                 case 'xs' :
34503                 case 'sm' :
34504                 case 'wide' :
34505                 case 'wide-thin' :
34506                     
34507                     box.push(item);
34508                     
34509                     break;
34510                 default :
34511                     break;
34512                     
34513             }
34514             
34515         }, this);
34516         
34517         if(box.length){
34518             boxes.push(box);
34519             box = [];
34520         }
34521         
34522         var filterPattern = function(box, length)
34523         {
34524             if(!box.length){
34525                 return;
34526             }
34527             
34528             var match = false;
34529             
34530             var pattern = box.slice(0, length);
34531             
34532             var format = [];
34533             
34534             Roo.each(pattern, function(i){
34535                 format.push(i.size);
34536             }, this);
34537             
34538             Roo.each(standard, function(s){
34539                 
34540                 if(String(s) != String(format)){
34541                     return;
34542                 }
34543                 
34544                 match = true;
34545                 return false;
34546                 
34547             }, this);
34548             
34549             if(!match && length == 1){
34550                 return;
34551             }
34552             
34553             if(!match){
34554                 filterPattern(box, length - 1);
34555                 return;
34556             }
34557                 
34558             queue.push(pattern);
34559
34560             box = box.slice(length, box.length);
34561
34562             filterPattern(box, 4);
34563
34564             return;
34565             
34566         }
34567         
34568         Roo.each(boxes, function(box, k){
34569             
34570             if(!box.length){
34571                 return;
34572             }
34573             
34574             if(box.length == 1){
34575                 queue.push(box);
34576                 return;
34577             }
34578             
34579             filterPattern(box, 4);
34580             
34581         }, this);
34582         
34583         
34584         var prune = [];
34585         
34586         var pos = this.el.getBox(true);
34587         
34588         var minX = pos.x;
34589         
34590         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34591         
34592         var hit_end = false;
34593         
34594         Roo.each(queue, function(box){
34595             
34596             if(hit_end){
34597                 
34598                 Roo.each(box, function(b){
34599                 
34600                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34601                     b.el.hide();
34602
34603                 }, this);
34604
34605                 return;
34606             }
34607             
34608             var mx = 0;
34609             
34610             Roo.each(box, function(b){
34611                 
34612                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34613                 b.el.show();
34614
34615                 mx = Math.max(mx, b.x);
34616                 
34617             }, this);
34618             
34619             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34620             
34621             if(maxX < minX){
34622                 
34623                 Roo.each(box, function(b){
34624                 
34625                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34626                     b.el.hide();
34627                     
34628                 }, this);
34629                 
34630                 hit_end = true;
34631                 
34632                 return;
34633             }
34634             
34635             prune.push(box);
34636             
34637         }, this);
34638         
34639         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34640     },
34641     
34642     /** Sets position of item in DOM
34643     * @param {Element} item
34644     * @param {Number} x - horizontal position
34645     * @param {Number} y - vertical position
34646     * @param {Boolean} isInstant - disables transitions
34647     */
34648     _processVerticalLayoutQueue : function( queue, isInstant )
34649     {
34650         var pos = this.el.getBox(true);
34651         var x = pos.x;
34652         var y = pos.y;
34653         var maxY = [];
34654         
34655         for (var i = 0; i < this.cols; i++){
34656             maxY[i] = pos.y;
34657         }
34658         
34659         Roo.each(queue, function(box, k){
34660             
34661             var col = k % this.cols;
34662             
34663             Roo.each(box, function(b,kk){
34664                 
34665                 b.el.position('absolute');
34666                 
34667                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34668                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34669                 
34670                 if(b.size == 'md-left' || b.size == 'md-right'){
34671                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34672                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34673                 }
34674                 
34675                 b.el.setWidth(width);
34676                 b.el.setHeight(height);
34677                 // iframe?
34678                 b.el.select('iframe',true).setSize(width,height);
34679                 
34680             }, this);
34681             
34682             for (var i = 0; i < this.cols; i++){
34683                 
34684                 if(maxY[i] < maxY[col]){
34685                     col = i;
34686                     continue;
34687                 }
34688                 
34689                 col = Math.min(col, i);
34690                 
34691             }
34692             
34693             x = pos.x + col * (this.colWidth + this.padWidth);
34694             
34695             y = maxY[col];
34696             
34697             var positions = [];
34698             
34699             switch (box.length){
34700                 case 1 :
34701                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34702                     break;
34703                 case 2 :
34704                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34705                     break;
34706                 case 3 :
34707                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34708                     break;
34709                 case 4 :
34710                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34711                     break;
34712                 default :
34713                     break;
34714             }
34715             
34716             Roo.each(box, function(b,kk){
34717                 
34718                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34719                 
34720                 var sz = b.el.getSize();
34721                 
34722                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34723                 
34724             }, this);
34725             
34726         }, this);
34727         
34728         var mY = 0;
34729         
34730         for (var i = 0; i < this.cols; i++){
34731             mY = Math.max(mY, maxY[i]);
34732         }
34733         
34734         this.el.setHeight(mY - pos.y);
34735         
34736     },
34737     
34738 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34739 //    {
34740 //        var pos = this.el.getBox(true);
34741 //        var x = pos.x;
34742 //        var y = pos.y;
34743 //        var maxX = pos.right;
34744 //        
34745 //        var maxHeight = 0;
34746 //        
34747 //        Roo.each(items, function(item, k){
34748 //            
34749 //            var c = k % 2;
34750 //            
34751 //            item.el.position('absolute');
34752 //                
34753 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34754 //
34755 //            item.el.setWidth(width);
34756 //
34757 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34758 //
34759 //            item.el.setHeight(height);
34760 //            
34761 //            if(c == 0){
34762 //                item.el.setXY([x, y], isInstant ? false : true);
34763 //            } else {
34764 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34765 //            }
34766 //            
34767 //            y = y + height + this.alternativePadWidth;
34768 //            
34769 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34770 //            
34771 //        }, this);
34772 //        
34773 //        this.el.setHeight(maxHeight);
34774 //        
34775 //    },
34776     
34777     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34778     {
34779         var pos = this.el.getBox(true);
34780         
34781         var minX = pos.x;
34782         var minY = pos.y;
34783         
34784         var maxX = pos.right;
34785         
34786         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34787         
34788         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34789         
34790         Roo.each(queue, function(box, k){
34791             
34792             Roo.each(box, function(b, kk){
34793                 
34794                 b.el.position('absolute');
34795                 
34796                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34797                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34798                 
34799                 if(b.size == 'md-left' || b.size == 'md-right'){
34800                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34801                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34802                 }
34803                 
34804                 b.el.setWidth(width);
34805                 b.el.setHeight(height);
34806                 
34807             }, this);
34808             
34809             if(!box.length){
34810                 return;
34811             }
34812             
34813             var positions = [];
34814             
34815             switch (box.length){
34816                 case 1 :
34817                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34818                     break;
34819                 case 2 :
34820                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34821                     break;
34822                 case 3 :
34823                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34824                     break;
34825                 case 4 :
34826                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34827                     break;
34828                 default :
34829                     break;
34830             }
34831             
34832             Roo.each(box, function(b,kk){
34833                 
34834                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34835                 
34836                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34837                 
34838             }, this);
34839             
34840         }, this);
34841         
34842     },
34843     
34844     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34845     {
34846         Roo.each(eItems, function(b,k){
34847             
34848             b.size = (k == 0) ? 'sm' : 'xs';
34849             b.x = (k == 0) ? 2 : 1;
34850             b.y = (k == 0) ? 2 : 1;
34851             
34852             b.el.position('absolute');
34853             
34854             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34855                 
34856             b.el.setWidth(width);
34857             
34858             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34859             
34860             b.el.setHeight(height);
34861             
34862         }, this);
34863
34864         var positions = [];
34865         
34866         positions.push({
34867             x : maxX - this.unitWidth * 2 - this.gutter,
34868             y : minY
34869         });
34870         
34871         positions.push({
34872             x : maxX - this.unitWidth,
34873             y : minY + (this.unitWidth + this.gutter) * 2
34874         });
34875         
34876         positions.push({
34877             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34878             y : minY
34879         });
34880         
34881         Roo.each(eItems, function(b,k){
34882             
34883             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34884
34885         }, this);
34886         
34887     },
34888     
34889     getVerticalOneBoxColPositions : function(x, y, box)
34890     {
34891         var pos = [];
34892         
34893         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34894         
34895         if(box[0].size == 'md-left'){
34896             rand = 0;
34897         }
34898         
34899         if(box[0].size == 'md-right'){
34900             rand = 1;
34901         }
34902         
34903         pos.push({
34904             x : x + (this.unitWidth + this.gutter) * rand,
34905             y : y
34906         });
34907         
34908         return pos;
34909     },
34910     
34911     getVerticalTwoBoxColPositions : function(x, y, box)
34912     {
34913         var pos = [];
34914         
34915         if(box[0].size == 'xs'){
34916             
34917             pos.push({
34918                 x : x,
34919                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34920             });
34921
34922             pos.push({
34923                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34924                 y : y
34925             });
34926             
34927             return pos;
34928             
34929         }
34930         
34931         pos.push({
34932             x : x,
34933             y : y
34934         });
34935
34936         pos.push({
34937             x : x + (this.unitWidth + this.gutter) * 2,
34938             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34939         });
34940         
34941         return pos;
34942         
34943     },
34944     
34945     getVerticalThreeBoxColPositions : function(x, y, box)
34946     {
34947         var pos = [];
34948         
34949         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34950             
34951             pos.push({
34952                 x : x,
34953                 y : y
34954             });
34955
34956             pos.push({
34957                 x : x + (this.unitWidth + this.gutter) * 1,
34958                 y : y
34959             });
34960             
34961             pos.push({
34962                 x : x + (this.unitWidth + this.gutter) * 2,
34963                 y : y
34964             });
34965             
34966             return pos;
34967             
34968         }
34969         
34970         if(box[0].size == 'xs' && box[1].size == 'xs'){
34971             
34972             pos.push({
34973                 x : x,
34974                 y : y
34975             });
34976
34977             pos.push({
34978                 x : x,
34979                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34980             });
34981             
34982             pos.push({
34983                 x : x + (this.unitWidth + this.gutter) * 1,
34984                 y : y
34985             });
34986             
34987             return pos;
34988             
34989         }
34990         
34991         pos.push({
34992             x : x,
34993             y : y
34994         });
34995
34996         pos.push({
34997             x : x + (this.unitWidth + this.gutter) * 2,
34998             y : y
34999         });
35000
35001         pos.push({
35002             x : x + (this.unitWidth + this.gutter) * 2,
35003             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35004         });
35005             
35006         return pos;
35007         
35008     },
35009     
35010     getVerticalFourBoxColPositions : function(x, y, box)
35011     {
35012         var pos = [];
35013         
35014         if(box[0].size == 'xs'){
35015             
35016             pos.push({
35017                 x : x,
35018                 y : y
35019             });
35020
35021             pos.push({
35022                 x : x,
35023                 y : y + (this.unitHeight + this.gutter) * 1
35024             });
35025             
35026             pos.push({
35027                 x : x,
35028                 y : y + (this.unitHeight + this.gutter) * 2
35029             });
35030             
35031             pos.push({
35032                 x : x + (this.unitWidth + this.gutter) * 1,
35033                 y : y
35034             });
35035             
35036             return pos;
35037             
35038         }
35039         
35040         pos.push({
35041             x : x,
35042             y : y
35043         });
35044
35045         pos.push({
35046             x : x + (this.unitWidth + this.gutter) * 2,
35047             y : y
35048         });
35049
35050         pos.push({
35051             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35052             y : y + (this.unitHeight + this.gutter) * 1
35053         });
35054
35055         pos.push({
35056             x : x + (this.unitWidth + this.gutter) * 2,
35057             y : y + (this.unitWidth + this.gutter) * 2
35058         });
35059
35060         return pos;
35061         
35062     },
35063     
35064     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35065     {
35066         var pos = [];
35067         
35068         if(box[0].size == 'md-left'){
35069             pos.push({
35070                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35071                 y : minY
35072             });
35073             
35074             return pos;
35075         }
35076         
35077         if(box[0].size == 'md-right'){
35078             pos.push({
35079                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35080                 y : minY + (this.unitWidth + this.gutter) * 1
35081             });
35082             
35083             return pos;
35084         }
35085         
35086         var rand = Math.floor(Math.random() * (4 - box[0].y));
35087         
35088         pos.push({
35089             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35090             y : minY + (this.unitWidth + this.gutter) * rand
35091         });
35092         
35093         return pos;
35094         
35095     },
35096     
35097     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35098     {
35099         var pos = [];
35100         
35101         if(box[0].size == 'xs'){
35102             
35103             pos.push({
35104                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35105                 y : minY
35106             });
35107
35108             pos.push({
35109                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35110                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35111             });
35112             
35113             return pos;
35114             
35115         }
35116         
35117         pos.push({
35118             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35119             y : minY
35120         });
35121
35122         pos.push({
35123             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35124             y : minY + (this.unitWidth + this.gutter) * 2
35125         });
35126         
35127         return pos;
35128         
35129     },
35130     
35131     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35132     {
35133         var pos = [];
35134         
35135         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35136             
35137             pos.push({
35138                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35139                 y : minY
35140             });
35141
35142             pos.push({
35143                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35144                 y : minY + (this.unitWidth + this.gutter) * 1
35145             });
35146             
35147             pos.push({
35148                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35149                 y : minY + (this.unitWidth + this.gutter) * 2
35150             });
35151             
35152             return pos;
35153             
35154         }
35155         
35156         if(box[0].size == 'xs' && box[1].size == 'xs'){
35157             
35158             pos.push({
35159                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35160                 y : minY
35161             });
35162
35163             pos.push({
35164                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35165                 y : minY
35166             });
35167             
35168             pos.push({
35169                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35170                 y : minY + (this.unitWidth + this.gutter) * 1
35171             });
35172             
35173             return pos;
35174             
35175         }
35176         
35177         pos.push({
35178             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35179             y : minY
35180         });
35181
35182         pos.push({
35183             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35184             y : minY + (this.unitWidth + this.gutter) * 2
35185         });
35186
35187         pos.push({
35188             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35189             y : minY + (this.unitWidth + this.gutter) * 2
35190         });
35191             
35192         return pos;
35193         
35194     },
35195     
35196     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35197     {
35198         var pos = [];
35199         
35200         if(box[0].size == 'xs'){
35201             
35202             pos.push({
35203                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35204                 y : minY
35205             });
35206
35207             pos.push({
35208                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35209                 y : minY
35210             });
35211             
35212             pos.push({
35213                 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),
35214                 y : minY
35215             });
35216             
35217             pos.push({
35218                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35219                 y : minY + (this.unitWidth + this.gutter) * 1
35220             });
35221             
35222             return pos;
35223             
35224         }
35225         
35226         pos.push({
35227             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35228             y : minY
35229         });
35230         
35231         pos.push({
35232             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35233             y : minY + (this.unitWidth + this.gutter) * 2
35234         });
35235         
35236         pos.push({
35237             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35238             y : minY + (this.unitWidth + this.gutter) * 2
35239         });
35240         
35241         pos.push({
35242             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),
35243             y : minY + (this.unitWidth + this.gutter) * 2
35244         });
35245
35246         return pos;
35247         
35248     },
35249     
35250     /**
35251     * remove a Masonry Brick
35252     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35253     */
35254     removeBrick : function(brick_id)
35255     {
35256         if (!brick_id) {
35257             return;
35258         }
35259         
35260         for (var i = 0; i<this.bricks.length; i++) {
35261             if (this.bricks[i].id == brick_id) {
35262                 this.bricks.splice(i,1);
35263                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35264                 this.initial();
35265             }
35266         }
35267     },
35268     
35269     /**
35270     * adds a Masonry Brick
35271     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35272     */
35273     addBrick : function(cfg)
35274     {
35275         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35276         //this.register(cn);
35277         cn.parentId = this.id;
35278         cn.render(this.el);
35279         return cn;
35280     },
35281     
35282     /**
35283     * register a Masonry Brick
35284     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35285     */
35286     
35287     register : function(brick)
35288     {
35289         this.bricks.push(brick);
35290         brick.masonryId = this.id;
35291     },
35292     
35293     /**
35294     * clear all the Masonry Brick
35295     */
35296     clearAll : function()
35297     {
35298         this.bricks = [];
35299         //this.getChildContainer().dom.innerHTML = "";
35300         this.el.dom.innerHTML = '';
35301     },
35302     
35303     getSelected : function()
35304     {
35305         if (!this.selectedBrick) {
35306             return false;
35307         }
35308         
35309         return this.selectedBrick;
35310     }
35311 });
35312
35313 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35314     
35315     groups: {},
35316      /**
35317     * register a Masonry Layout
35318     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35319     */
35320     
35321     register : function(layout)
35322     {
35323         this.groups[layout.id] = layout;
35324     },
35325     /**
35326     * fetch a  Masonry Layout based on the masonry layout ID
35327     * @param {string} the masonry layout to add
35328     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35329     */
35330     
35331     get: function(layout_id) {
35332         if (typeof(this.groups[layout_id]) == 'undefined') {
35333             return false;
35334         }
35335         return this.groups[layout_id] ;
35336     }
35337     
35338     
35339     
35340 });
35341
35342  
35343
35344  /**
35345  *
35346  * This is based on 
35347  * http://masonry.desandro.com
35348  *
35349  * The idea is to render all the bricks based on vertical width...
35350  *
35351  * The original code extends 'outlayer' - we might need to use that....
35352  * 
35353  */
35354
35355
35356 /**
35357  * @class Roo.bootstrap.LayoutMasonryAuto
35358  * @extends Roo.bootstrap.Component
35359  * Bootstrap Layout Masonry class
35360  * 
35361  * @constructor
35362  * Create a new Element
35363  * @param {Object} config The config object
35364  */
35365
35366 Roo.bootstrap.LayoutMasonryAuto = function(config){
35367     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35368 };
35369
35370 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35371     
35372       /**
35373      * @cfg {Boolean} isFitWidth  - resize the width..
35374      */   
35375     isFitWidth : false,  // options..
35376     /**
35377      * @cfg {Boolean} isOriginLeft = left align?
35378      */   
35379     isOriginLeft : true,
35380     /**
35381      * @cfg {Boolean} isOriginTop = top align?
35382      */   
35383     isOriginTop : false,
35384     /**
35385      * @cfg {Boolean} isLayoutInstant = no animation?
35386      */   
35387     isLayoutInstant : false, // needed?
35388     /**
35389      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35390      */   
35391     isResizingContainer : true,
35392     /**
35393      * @cfg {Number} columnWidth  width of the columns 
35394      */   
35395     
35396     columnWidth : 0,
35397     
35398     /**
35399      * @cfg {Number} maxCols maximum number of columns
35400      */   
35401     
35402     maxCols: 0,
35403     /**
35404      * @cfg {Number} padHeight padding below box..
35405      */   
35406     
35407     padHeight : 10, 
35408     
35409     /**
35410      * @cfg {Boolean} isAutoInitial defalut true
35411      */   
35412     
35413     isAutoInitial : true, 
35414     
35415     // private?
35416     gutter : 0,
35417     
35418     containerWidth: 0,
35419     initialColumnWidth : 0,
35420     currentSize : null,
35421     
35422     colYs : null, // array.
35423     maxY : 0,
35424     padWidth: 10,
35425     
35426     
35427     tag: 'div',
35428     cls: '',
35429     bricks: null, //CompositeElement
35430     cols : 0, // array?
35431     // element : null, // wrapped now this.el
35432     _isLayoutInited : null, 
35433     
35434     
35435     getAutoCreate : function(){
35436         
35437         var cfg = {
35438             tag: this.tag,
35439             cls: 'blog-masonary-wrapper ' + this.cls,
35440             cn : {
35441                 cls : 'mas-boxes masonary'
35442             }
35443         };
35444         
35445         return cfg;
35446     },
35447     
35448     getChildContainer: function( )
35449     {
35450         if (this.boxesEl) {
35451             return this.boxesEl;
35452         }
35453         
35454         this.boxesEl = this.el.select('.mas-boxes').first();
35455         
35456         return this.boxesEl;
35457     },
35458     
35459     
35460     initEvents : function()
35461     {
35462         var _this = this;
35463         
35464         if(this.isAutoInitial){
35465             Roo.log('hook children rendered');
35466             this.on('childrenrendered', function() {
35467                 Roo.log('children rendered');
35468                 _this.initial();
35469             } ,this);
35470         }
35471         
35472     },
35473     
35474     initial : function()
35475     {
35476         this.reloadItems();
35477
35478         this.currentSize = this.el.getBox(true);
35479
35480         /// was window resize... - let's see if this works..
35481         Roo.EventManager.onWindowResize(this.resize, this); 
35482
35483         if(!this.isAutoInitial){
35484             this.layout();
35485             return;
35486         }
35487         
35488         this.layout.defer(500,this);
35489     },
35490     
35491     reloadItems: function()
35492     {
35493         this.bricks = this.el.select('.masonry-brick', true);
35494         
35495         this.bricks.each(function(b) {
35496             //Roo.log(b.getSize());
35497             if (!b.attr('originalwidth')) {
35498                 b.attr('originalwidth',  b.getSize().width);
35499             }
35500             
35501         });
35502         
35503         Roo.log(this.bricks.elements.length);
35504     },
35505     
35506     resize : function()
35507     {
35508         Roo.log('resize');
35509         var cs = this.el.getBox(true);
35510         
35511         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35512             Roo.log("no change in with or X");
35513             return;
35514         }
35515         this.currentSize = cs;
35516         this.layout();
35517     },
35518     
35519     layout : function()
35520     {
35521          Roo.log('layout');
35522         this._resetLayout();
35523         //this._manageStamps();
35524       
35525         // don't animate first layout
35526         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35527         this.layoutItems( isInstant );
35528       
35529         // flag for initalized
35530         this._isLayoutInited = true;
35531     },
35532     
35533     layoutItems : function( isInstant )
35534     {
35535         //var items = this._getItemsForLayout( this.items );
35536         // original code supports filtering layout items.. we just ignore it..
35537         
35538         this._layoutItems( this.bricks , isInstant );
35539       
35540         this._postLayout();
35541     },
35542     _layoutItems : function ( items , isInstant)
35543     {
35544        //this.fireEvent( 'layout', this, items );
35545     
35546
35547         if ( !items || !items.elements.length ) {
35548           // no items, emit event with empty array
35549             return;
35550         }
35551
35552         var queue = [];
35553         items.each(function(item) {
35554             Roo.log("layout item");
35555             Roo.log(item);
35556             // get x/y object from method
35557             var position = this._getItemLayoutPosition( item );
35558             // enqueue
35559             position.item = item;
35560             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35561             queue.push( position );
35562         }, this);
35563       
35564         this._processLayoutQueue( queue );
35565     },
35566     /** Sets position of item in DOM
35567     * @param {Element} item
35568     * @param {Number} x - horizontal position
35569     * @param {Number} y - vertical position
35570     * @param {Boolean} isInstant - disables transitions
35571     */
35572     _processLayoutQueue : function( queue )
35573     {
35574         for ( var i=0, len = queue.length; i < len; i++ ) {
35575             var obj = queue[i];
35576             obj.item.position('absolute');
35577             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35578         }
35579     },
35580       
35581     
35582     /**
35583     * Any logic you want to do after each layout,
35584     * i.e. size the container
35585     */
35586     _postLayout : function()
35587     {
35588         this.resizeContainer();
35589     },
35590     
35591     resizeContainer : function()
35592     {
35593         if ( !this.isResizingContainer ) {
35594             return;
35595         }
35596         var size = this._getContainerSize();
35597         if ( size ) {
35598             this.el.setSize(size.width,size.height);
35599             this.boxesEl.setSize(size.width,size.height);
35600         }
35601     },
35602     
35603     
35604     
35605     _resetLayout : function()
35606     {
35607         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35608         this.colWidth = this.el.getWidth();
35609         //this.gutter = this.el.getWidth(); 
35610         
35611         this.measureColumns();
35612
35613         // reset column Y
35614         var i = this.cols;
35615         this.colYs = [];
35616         while (i--) {
35617             this.colYs.push( 0 );
35618         }
35619     
35620         this.maxY = 0;
35621     },
35622
35623     measureColumns : function()
35624     {
35625         this.getContainerWidth();
35626       // if columnWidth is 0, default to outerWidth of first item
35627         if ( !this.columnWidth ) {
35628             var firstItem = this.bricks.first();
35629             Roo.log(firstItem);
35630             this.columnWidth  = this.containerWidth;
35631             if (firstItem && firstItem.attr('originalwidth') ) {
35632                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35633             }
35634             // columnWidth fall back to item of first element
35635             Roo.log("set column width?");
35636                         this.initialColumnWidth = this.columnWidth  ;
35637
35638             // if first elem has no width, default to size of container
35639             
35640         }
35641         
35642         
35643         if (this.initialColumnWidth) {
35644             this.columnWidth = this.initialColumnWidth;
35645         }
35646         
35647         
35648             
35649         // column width is fixed at the top - however if container width get's smaller we should
35650         // reduce it...
35651         
35652         // this bit calcs how man columns..
35653             
35654         var columnWidth = this.columnWidth += this.gutter;
35655       
35656         // calculate columns
35657         var containerWidth = this.containerWidth + this.gutter;
35658         
35659         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35660         // fix rounding errors, typically with gutters
35661         var excess = columnWidth - containerWidth % columnWidth;
35662         
35663         
35664         // if overshoot is less than a pixel, round up, otherwise floor it
35665         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35666         cols = Math[ mathMethod ]( cols );
35667         this.cols = Math.max( cols, 1 );
35668         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35669         
35670          // padding positioning..
35671         var totalColWidth = this.cols * this.columnWidth;
35672         var padavail = this.containerWidth - totalColWidth;
35673         // so for 2 columns - we need 3 'pads'
35674         
35675         var padNeeded = (1+this.cols) * this.padWidth;
35676         
35677         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35678         
35679         this.columnWidth += padExtra
35680         //this.padWidth = Math.floor(padavail /  ( this.cols));
35681         
35682         // adjust colum width so that padding is fixed??
35683         
35684         // we have 3 columns ... total = width * 3
35685         // we have X left over... that should be used by 
35686         
35687         //if (this.expandC) {
35688             
35689         //}
35690         
35691         
35692         
35693     },
35694     
35695     getContainerWidth : function()
35696     {
35697        /* // container is parent if fit width
35698         var container = this.isFitWidth ? this.element.parentNode : this.element;
35699         // check that this.size and size are there
35700         // IE8 triggers resize on body size change, so they might not be
35701         
35702         var size = getSize( container );  //FIXME
35703         this.containerWidth = size && size.innerWidth; //FIXME
35704         */
35705          
35706         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35707         
35708     },
35709     
35710     _getItemLayoutPosition : function( item )  // what is item?
35711     {
35712         // we resize the item to our columnWidth..
35713       
35714         item.setWidth(this.columnWidth);
35715         item.autoBoxAdjust  = false;
35716         
35717         var sz = item.getSize();
35718  
35719         // how many columns does this brick span
35720         var remainder = this.containerWidth % this.columnWidth;
35721         
35722         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35723         // round if off by 1 pixel, otherwise use ceil
35724         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35725         colSpan = Math.min( colSpan, this.cols );
35726         
35727         // normally this should be '1' as we dont' currently allow multi width columns..
35728         
35729         var colGroup = this._getColGroup( colSpan );
35730         // get the minimum Y value from the columns
35731         var minimumY = Math.min.apply( Math, colGroup );
35732         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35733         
35734         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35735          
35736         // position the brick
35737         var position = {
35738             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35739             y: this.currentSize.y + minimumY + this.padHeight
35740         };
35741         
35742         Roo.log(position);
35743         // apply setHeight to necessary columns
35744         var setHeight = minimumY + sz.height + this.padHeight;
35745         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35746         
35747         var setSpan = this.cols + 1 - colGroup.length;
35748         for ( var i = 0; i < setSpan; i++ ) {
35749           this.colYs[ shortColIndex + i ] = setHeight ;
35750         }
35751       
35752         return position;
35753     },
35754     
35755     /**
35756      * @param {Number} colSpan - number of columns the element spans
35757      * @returns {Array} colGroup
35758      */
35759     _getColGroup : function( colSpan )
35760     {
35761         if ( colSpan < 2 ) {
35762           // if brick spans only one column, use all the column Ys
35763           return this.colYs;
35764         }
35765       
35766         var colGroup = [];
35767         // how many different places could this brick fit horizontally
35768         var groupCount = this.cols + 1 - colSpan;
35769         // for each group potential horizontal position
35770         for ( var i = 0; i < groupCount; i++ ) {
35771           // make an array of colY values for that one group
35772           var groupColYs = this.colYs.slice( i, i + colSpan );
35773           // and get the max value of the array
35774           colGroup[i] = Math.max.apply( Math, groupColYs );
35775         }
35776         return colGroup;
35777     },
35778     /*
35779     _manageStamp : function( stamp )
35780     {
35781         var stampSize =  stamp.getSize();
35782         var offset = stamp.getBox();
35783         // get the columns that this stamp affects
35784         var firstX = this.isOriginLeft ? offset.x : offset.right;
35785         var lastX = firstX + stampSize.width;
35786         var firstCol = Math.floor( firstX / this.columnWidth );
35787         firstCol = Math.max( 0, firstCol );
35788         
35789         var lastCol = Math.floor( lastX / this.columnWidth );
35790         // lastCol should not go over if multiple of columnWidth #425
35791         lastCol -= lastX % this.columnWidth ? 0 : 1;
35792         lastCol = Math.min( this.cols - 1, lastCol );
35793         
35794         // set colYs to bottom of the stamp
35795         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35796             stampSize.height;
35797             
35798         for ( var i = firstCol; i <= lastCol; i++ ) {
35799           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35800         }
35801     },
35802     */
35803     
35804     _getContainerSize : function()
35805     {
35806         this.maxY = Math.max.apply( Math, this.colYs );
35807         var size = {
35808             height: this.maxY
35809         };
35810       
35811         if ( this.isFitWidth ) {
35812             size.width = this._getContainerFitWidth();
35813         }
35814       
35815         return size;
35816     },
35817     
35818     _getContainerFitWidth : function()
35819     {
35820         var unusedCols = 0;
35821         // count unused columns
35822         var i = this.cols;
35823         while ( --i ) {
35824           if ( this.colYs[i] !== 0 ) {
35825             break;
35826           }
35827           unusedCols++;
35828         }
35829         // fit container to columns that have been used
35830         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35831     },
35832     
35833     needsResizeLayout : function()
35834     {
35835         var previousWidth = this.containerWidth;
35836         this.getContainerWidth();
35837         return previousWidth !== this.containerWidth;
35838     }
35839  
35840 });
35841
35842  
35843
35844  /*
35845  * - LGPL
35846  *
35847  * element
35848  * 
35849  */
35850
35851 /**
35852  * @class Roo.bootstrap.MasonryBrick
35853  * @extends Roo.bootstrap.Component
35854  * Bootstrap MasonryBrick class
35855  * 
35856  * @constructor
35857  * Create a new MasonryBrick
35858  * @param {Object} config The config object
35859  */
35860
35861 Roo.bootstrap.MasonryBrick = function(config){
35862     
35863     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35864     
35865     Roo.bootstrap.MasonryBrick.register(this);
35866     
35867     this.addEvents({
35868         // raw events
35869         /**
35870          * @event click
35871          * When a MasonryBrick is clcik
35872          * @param {Roo.bootstrap.MasonryBrick} this
35873          * @param {Roo.EventObject} e
35874          */
35875         "click" : true
35876     });
35877 };
35878
35879 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35880     
35881     /**
35882      * @cfg {String} title
35883      */   
35884     title : '',
35885     /**
35886      * @cfg {String} html
35887      */   
35888     html : '',
35889     /**
35890      * @cfg {String} bgimage
35891      */   
35892     bgimage : '',
35893     /**
35894      * @cfg {String} videourl
35895      */   
35896     videourl : '',
35897     /**
35898      * @cfg {String} cls
35899      */   
35900     cls : '',
35901     /**
35902      * @cfg {String} href
35903      */   
35904     href : '',
35905     /**
35906      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35907      */   
35908     size : 'xs',
35909     
35910     /**
35911      * @cfg {String} placetitle (center|bottom)
35912      */   
35913     placetitle : '',
35914     
35915     /**
35916      * @cfg {Boolean} isFitContainer defalut true
35917      */   
35918     isFitContainer : true, 
35919     
35920     /**
35921      * @cfg {Boolean} preventDefault defalut false
35922      */   
35923     preventDefault : false, 
35924     
35925     /**
35926      * @cfg {Boolean} inverse defalut false
35927      */   
35928     maskInverse : false, 
35929     
35930     getAutoCreate : function()
35931     {
35932         if(!this.isFitContainer){
35933             return this.getSplitAutoCreate();
35934         }
35935         
35936         var cls = 'masonry-brick masonry-brick-full';
35937         
35938         if(this.href.length){
35939             cls += ' masonry-brick-link';
35940         }
35941         
35942         if(this.bgimage.length){
35943             cls += ' masonry-brick-image';
35944         }
35945         
35946         if(this.maskInverse){
35947             cls += ' mask-inverse';
35948         }
35949         
35950         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35951             cls += ' enable-mask';
35952         }
35953         
35954         if(this.size){
35955             cls += ' masonry-' + this.size + '-brick';
35956         }
35957         
35958         if(this.placetitle.length){
35959             
35960             switch (this.placetitle) {
35961                 case 'center' :
35962                     cls += ' masonry-center-title';
35963                     break;
35964                 case 'bottom' :
35965                     cls += ' masonry-bottom-title';
35966                     break;
35967                 default:
35968                     break;
35969             }
35970             
35971         } else {
35972             if(!this.html.length && !this.bgimage.length){
35973                 cls += ' masonry-center-title';
35974             }
35975
35976             if(!this.html.length && this.bgimage.length){
35977                 cls += ' masonry-bottom-title';
35978             }
35979         }
35980         
35981         if(this.cls){
35982             cls += ' ' + this.cls;
35983         }
35984         
35985         var cfg = {
35986             tag: (this.href.length) ? 'a' : 'div',
35987             cls: cls,
35988             cn: [
35989                 {
35990                     tag: 'div',
35991                     cls: 'masonry-brick-mask'
35992                 },
35993                 {
35994                     tag: 'div',
35995                     cls: 'masonry-brick-paragraph',
35996                     cn: []
35997                 }
35998             ]
35999         };
36000         
36001         if(this.href.length){
36002             cfg.href = this.href;
36003         }
36004         
36005         var cn = cfg.cn[1].cn;
36006         
36007         if(this.title.length){
36008             cn.push({
36009                 tag: 'h4',
36010                 cls: 'masonry-brick-title',
36011                 html: this.title
36012             });
36013         }
36014         
36015         if(this.html.length){
36016             cn.push({
36017                 tag: 'p',
36018                 cls: 'masonry-brick-text',
36019                 html: this.html
36020             });
36021         }
36022         
36023         if (!this.title.length && !this.html.length) {
36024             cfg.cn[1].cls += ' hide';
36025         }
36026         
36027         if(this.bgimage.length){
36028             cfg.cn.push({
36029                 tag: 'img',
36030                 cls: 'masonry-brick-image-view',
36031                 src: this.bgimage
36032             });
36033         }
36034         
36035         if(this.videourl.length){
36036             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36037             // youtube support only?
36038             cfg.cn.push({
36039                 tag: 'iframe',
36040                 cls: 'masonry-brick-image-view',
36041                 src: vurl,
36042                 frameborder : 0,
36043                 allowfullscreen : true
36044             });
36045         }
36046         
36047         return cfg;
36048         
36049     },
36050     
36051     getSplitAutoCreate : function()
36052     {
36053         var cls = 'masonry-brick masonry-brick-split';
36054         
36055         if(this.href.length){
36056             cls += ' masonry-brick-link';
36057         }
36058         
36059         if(this.bgimage.length){
36060             cls += ' masonry-brick-image';
36061         }
36062         
36063         if(this.size){
36064             cls += ' masonry-' + this.size + '-brick';
36065         }
36066         
36067         switch (this.placetitle) {
36068             case 'center' :
36069                 cls += ' masonry-center-title';
36070                 break;
36071             case 'bottom' :
36072                 cls += ' masonry-bottom-title';
36073                 break;
36074             default:
36075                 if(!this.bgimage.length){
36076                     cls += ' masonry-center-title';
36077                 }
36078
36079                 if(this.bgimage.length){
36080                     cls += ' masonry-bottom-title';
36081                 }
36082                 break;
36083         }
36084         
36085         if(this.cls){
36086             cls += ' ' + this.cls;
36087         }
36088         
36089         var cfg = {
36090             tag: (this.href.length) ? 'a' : 'div',
36091             cls: cls,
36092             cn: [
36093                 {
36094                     tag: 'div',
36095                     cls: 'masonry-brick-split-head',
36096                     cn: [
36097                         {
36098                             tag: 'div',
36099                             cls: 'masonry-brick-paragraph',
36100                             cn: []
36101                         }
36102                     ]
36103                 },
36104                 {
36105                     tag: 'div',
36106                     cls: 'masonry-brick-split-body',
36107                     cn: []
36108                 }
36109             ]
36110         };
36111         
36112         if(this.href.length){
36113             cfg.href = this.href;
36114         }
36115         
36116         if(this.title.length){
36117             cfg.cn[0].cn[0].cn.push({
36118                 tag: 'h4',
36119                 cls: 'masonry-brick-title',
36120                 html: this.title
36121             });
36122         }
36123         
36124         if(this.html.length){
36125             cfg.cn[1].cn.push({
36126                 tag: 'p',
36127                 cls: 'masonry-brick-text',
36128                 html: this.html
36129             });
36130         }
36131
36132         if(this.bgimage.length){
36133             cfg.cn[0].cn.push({
36134                 tag: 'img',
36135                 cls: 'masonry-brick-image-view',
36136                 src: this.bgimage
36137             });
36138         }
36139         
36140         if(this.videourl.length){
36141             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36142             // youtube support only?
36143             cfg.cn[0].cn.cn.push({
36144                 tag: 'iframe',
36145                 cls: 'masonry-brick-image-view',
36146                 src: vurl,
36147                 frameborder : 0,
36148                 allowfullscreen : true
36149             });
36150         }
36151         
36152         return cfg;
36153     },
36154     
36155     initEvents: function() 
36156     {
36157         switch (this.size) {
36158             case 'xs' :
36159                 this.x = 1;
36160                 this.y = 1;
36161                 break;
36162             case 'sm' :
36163                 this.x = 2;
36164                 this.y = 2;
36165                 break;
36166             case 'md' :
36167             case 'md-left' :
36168             case 'md-right' :
36169                 this.x = 3;
36170                 this.y = 3;
36171                 break;
36172             case 'tall' :
36173                 this.x = 2;
36174                 this.y = 3;
36175                 break;
36176             case 'wide' :
36177                 this.x = 3;
36178                 this.y = 2;
36179                 break;
36180             case 'wide-thin' :
36181                 this.x = 3;
36182                 this.y = 1;
36183                 break;
36184                         
36185             default :
36186                 break;
36187         }
36188         
36189         if(Roo.isTouch){
36190             this.el.on('touchstart', this.onTouchStart, this);
36191             this.el.on('touchmove', this.onTouchMove, this);
36192             this.el.on('touchend', this.onTouchEnd, this);
36193             this.el.on('contextmenu', this.onContextMenu, this);
36194         } else {
36195             this.el.on('mouseenter'  ,this.enter, this);
36196             this.el.on('mouseleave', this.leave, this);
36197             this.el.on('click', this.onClick, this);
36198         }
36199         
36200         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36201             this.parent().bricks.push(this);   
36202         }
36203         
36204     },
36205     
36206     onClick: function(e, el)
36207     {
36208         var time = this.endTimer - this.startTimer;
36209         // Roo.log(e.preventDefault());
36210         if(Roo.isTouch){
36211             if(time > 1000){
36212                 e.preventDefault();
36213                 return;
36214             }
36215         }
36216         
36217         if(!this.preventDefault){
36218             return;
36219         }
36220         
36221         e.preventDefault();
36222         
36223         if (this.activeClass != '') {
36224             this.selectBrick();
36225         }
36226         
36227         this.fireEvent('click', this, e);
36228     },
36229     
36230     enter: function(e, el)
36231     {
36232         e.preventDefault();
36233         
36234         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36235             return;
36236         }
36237         
36238         if(this.bgimage.length && this.html.length){
36239             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36240         }
36241     },
36242     
36243     leave: function(e, el)
36244     {
36245         e.preventDefault();
36246         
36247         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36248             return;
36249         }
36250         
36251         if(this.bgimage.length && this.html.length){
36252             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36253         }
36254     },
36255     
36256     onTouchStart: function(e, el)
36257     {
36258 //        e.preventDefault();
36259         
36260         this.touchmoved = false;
36261         
36262         if(!this.isFitContainer){
36263             return;
36264         }
36265         
36266         if(!this.bgimage.length || !this.html.length){
36267             return;
36268         }
36269         
36270         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36271         
36272         this.timer = new Date().getTime();
36273         
36274     },
36275     
36276     onTouchMove: function(e, el)
36277     {
36278         this.touchmoved = true;
36279     },
36280     
36281     onContextMenu : function(e,el)
36282     {
36283         e.preventDefault();
36284         e.stopPropagation();
36285         return false;
36286     },
36287     
36288     onTouchEnd: function(e, el)
36289     {
36290 //        e.preventDefault();
36291         
36292         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36293         
36294             this.leave(e,el);
36295             
36296             return;
36297         }
36298         
36299         if(!this.bgimage.length || !this.html.length){
36300             
36301             if(this.href.length){
36302                 window.location.href = this.href;
36303             }
36304             
36305             return;
36306         }
36307         
36308         if(!this.isFitContainer){
36309             return;
36310         }
36311         
36312         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36313         
36314         window.location.href = this.href;
36315     },
36316     
36317     //selection on single brick only
36318     selectBrick : function() {
36319         
36320         if (!this.parentId) {
36321             return;
36322         }
36323         
36324         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36325         var index = m.selectedBrick.indexOf(this.id);
36326         
36327         if ( index > -1) {
36328             m.selectedBrick.splice(index,1);
36329             this.el.removeClass(this.activeClass);
36330             return;
36331         }
36332         
36333         for(var i = 0; i < m.selectedBrick.length; i++) {
36334             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36335             b.el.removeClass(b.activeClass);
36336         }
36337         
36338         m.selectedBrick = [];
36339         
36340         m.selectedBrick.push(this.id);
36341         this.el.addClass(this.activeClass);
36342         return;
36343     },
36344     
36345     isSelected : function(){
36346         return this.el.hasClass(this.activeClass);
36347         
36348     }
36349 });
36350
36351 Roo.apply(Roo.bootstrap.MasonryBrick, {
36352     
36353     //groups: {},
36354     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36355      /**
36356     * register a Masonry Brick
36357     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36358     */
36359     
36360     register : function(brick)
36361     {
36362         //this.groups[brick.id] = brick;
36363         this.groups.add(brick.id, brick);
36364     },
36365     /**
36366     * fetch a  masonry brick based on the masonry brick ID
36367     * @param {string} the masonry brick to add
36368     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36369     */
36370     
36371     get: function(brick_id) 
36372     {
36373         // if (typeof(this.groups[brick_id]) == 'undefined') {
36374         //     return false;
36375         // }
36376         // return this.groups[brick_id] ;
36377         
36378         if(this.groups.key(brick_id)) {
36379             return this.groups.key(brick_id);
36380         }
36381         
36382         return false;
36383     }
36384     
36385     
36386     
36387 });
36388
36389  /*
36390  * - LGPL
36391  *
36392  * element
36393  * 
36394  */
36395
36396 /**
36397  * @class Roo.bootstrap.Brick
36398  * @extends Roo.bootstrap.Component
36399  * Bootstrap Brick class
36400  * 
36401  * @constructor
36402  * Create a new Brick
36403  * @param {Object} config The config object
36404  */
36405
36406 Roo.bootstrap.Brick = function(config){
36407     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36408     
36409     this.addEvents({
36410         // raw events
36411         /**
36412          * @event click
36413          * When a Brick is click
36414          * @param {Roo.bootstrap.Brick} this
36415          * @param {Roo.EventObject} e
36416          */
36417         "click" : true
36418     });
36419 };
36420
36421 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36422     
36423     /**
36424      * @cfg {String} title
36425      */   
36426     title : '',
36427     /**
36428      * @cfg {String} html
36429      */   
36430     html : '',
36431     /**
36432      * @cfg {String} bgimage
36433      */   
36434     bgimage : '',
36435     /**
36436      * @cfg {String} cls
36437      */   
36438     cls : '',
36439     /**
36440      * @cfg {String} href
36441      */   
36442     href : '',
36443     /**
36444      * @cfg {String} video
36445      */   
36446     video : '',
36447     /**
36448      * @cfg {Boolean} square
36449      */   
36450     square : true,
36451     
36452     getAutoCreate : function()
36453     {
36454         var cls = 'roo-brick';
36455         
36456         if(this.href.length){
36457             cls += ' roo-brick-link';
36458         }
36459         
36460         if(this.bgimage.length){
36461             cls += ' roo-brick-image';
36462         }
36463         
36464         if(!this.html.length && !this.bgimage.length){
36465             cls += ' roo-brick-center-title';
36466         }
36467         
36468         if(!this.html.length && this.bgimage.length){
36469             cls += ' roo-brick-bottom-title';
36470         }
36471         
36472         if(this.cls){
36473             cls += ' ' + this.cls;
36474         }
36475         
36476         var cfg = {
36477             tag: (this.href.length) ? 'a' : 'div',
36478             cls: cls,
36479             cn: [
36480                 {
36481                     tag: 'div',
36482                     cls: 'roo-brick-paragraph',
36483                     cn: []
36484                 }
36485             ]
36486         };
36487         
36488         if(this.href.length){
36489             cfg.href = this.href;
36490         }
36491         
36492         var cn = cfg.cn[0].cn;
36493         
36494         if(this.title.length){
36495             cn.push({
36496                 tag: 'h4',
36497                 cls: 'roo-brick-title',
36498                 html: this.title
36499             });
36500         }
36501         
36502         if(this.html.length){
36503             cn.push({
36504                 tag: 'p',
36505                 cls: 'roo-brick-text',
36506                 html: this.html
36507             });
36508         } else {
36509             cn.cls += ' hide';
36510         }
36511         
36512         if(this.bgimage.length){
36513             cfg.cn.push({
36514                 tag: 'img',
36515                 cls: 'roo-brick-image-view',
36516                 src: this.bgimage
36517             });
36518         }
36519         
36520         return cfg;
36521     },
36522     
36523     initEvents: function() 
36524     {
36525         if(this.title.length || this.html.length){
36526             this.el.on('mouseenter'  ,this.enter, this);
36527             this.el.on('mouseleave', this.leave, this);
36528         }
36529         
36530         Roo.EventManager.onWindowResize(this.resize, this); 
36531         
36532         if(this.bgimage.length){
36533             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36534             this.imageEl.on('load', this.onImageLoad, this);
36535             return;
36536         }
36537         
36538         this.resize();
36539     },
36540     
36541     onImageLoad : function()
36542     {
36543         this.resize();
36544     },
36545     
36546     resize : function()
36547     {
36548         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36549         
36550         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36551         
36552         if(this.bgimage.length){
36553             var image = this.el.select('.roo-brick-image-view', true).first();
36554             
36555             image.setWidth(paragraph.getWidth());
36556             
36557             if(this.square){
36558                 image.setHeight(paragraph.getWidth());
36559             }
36560             
36561             this.el.setHeight(image.getHeight());
36562             paragraph.setHeight(image.getHeight());
36563             
36564         }
36565         
36566     },
36567     
36568     enter: function(e, el)
36569     {
36570         e.preventDefault();
36571         
36572         if(this.bgimage.length){
36573             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36574             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36575         }
36576     },
36577     
36578     leave: function(e, el)
36579     {
36580         e.preventDefault();
36581         
36582         if(this.bgimage.length){
36583             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36584             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36585         }
36586     }
36587     
36588 });
36589
36590  
36591
36592  /*
36593  * - LGPL
36594  *
36595  * Number field 
36596  */
36597
36598 /**
36599  * @class Roo.bootstrap.form.NumberField
36600  * @extends Roo.bootstrap.form.Input
36601  * Bootstrap NumberField class
36602  * 
36603  * 
36604  * 
36605  * 
36606  * @constructor
36607  * Create a new NumberField
36608  * @param {Object} config The config object
36609  */
36610
36611 Roo.bootstrap.form.NumberField = function(config){
36612     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36613 };
36614
36615 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36616     
36617     /**
36618      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36619      */
36620     allowDecimals : true,
36621     /**
36622      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36623      */
36624     decimalSeparator : ".",
36625     /**
36626      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36627      */
36628     decimalPrecision : 2,
36629     /**
36630      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36631      */
36632     allowNegative : true,
36633     
36634     /**
36635      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36636      */
36637     allowZero: true,
36638     /**
36639      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36640      */
36641     minValue : Number.NEGATIVE_INFINITY,
36642     /**
36643      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36644      */
36645     maxValue : Number.MAX_VALUE,
36646     /**
36647      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36648      */
36649     minText : "The minimum value for this field is {0}",
36650     /**
36651      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36652      */
36653     maxText : "The maximum value for this field is {0}",
36654     /**
36655      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36656      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36657      */
36658     nanText : "{0} is not a valid number",
36659     /**
36660      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36661      */
36662     thousandsDelimiter : false,
36663     /**
36664      * @cfg {String} valueAlign alignment of value
36665      */
36666     valueAlign : "left",
36667
36668     getAutoCreate : function()
36669     {
36670         var hiddenInput = {
36671             tag: 'input',
36672             type: 'hidden',
36673             id: Roo.id(),
36674             cls: 'hidden-number-input'
36675         };
36676         
36677         if (this.name) {
36678             hiddenInput.name = this.name;
36679         }
36680         
36681         this.name = '';
36682         
36683         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36684         
36685         this.name = hiddenInput.name;
36686         
36687         if(cfg.cn.length > 0) {
36688             cfg.cn.push(hiddenInput);
36689         }
36690         
36691         return cfg;
36692     },
36693
36694     // private
36695     initEvents : function()
36696     {   
36697         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36698         
36699         var allowed = "0123456789";
36700         
36701         if(this.allowDecimals){
36702             allowed += this.decimalSeparator;
36703         }
36704         
36705         if(this.allowNegative){
36706             allowed += "-";
36707         }
36708         
36709         if(this.thousandsDelimiter) {
36710             allowed += ",";
36711         }
36712         
36713         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36714         
36715         var keyPress = function(e){
36716             
36717             var k = e.getKey();
36718             
36719             var c = e.getCharCode();
36720             
36721             if(
36722                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36723                     allowed.indexOf(String.fromCharCode(c)) === -1
36724             ){
36725                 e.stopEvent();
36726                 return;
36727             }
36728             
36729             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36730                 return;
36731             }
36732             
36733             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36734                 e.stopEvent();
36735             }
36736         };
36737         
36738         this.el.on("keypress", keyPress, this);
36739     },
36740     
36741     validateValue : function(value)
36742     {
36743         
36744         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36745             return false;
36746         }
36747         
36748         var num = this.parseValue(value);
36749         
36750         if(isNaN(num)){
36751             this.markInvalid(String.format(this.nanText, value));
36752             return false;
36753         }
36754         
36755         if(num < this.minValue){
36756             this.markInvalid(String.format(this.minText, this.minValue));
36757             return false;
36758         }
36759         
36760         if(num > this.maxValue){
36761             this.markInvalid(String.format(this.maxText, this.maxValue));
36762             return false;
36763         }
36764         
36765         return true;
36766     },
36767
36768     getValue : function()
36769     {
36770         var v = this.hiddenEl().getValue();
36771         
36772         return this.fixPrecision(this.parseValue(v));
36773     },
36774
36775     parseValue : function(value)
36776     {
36777         if(this.thousandsDelimiter) {
36778             value += "";
36779             r = new RegExp(",", "g");
36780             value = value.replace(r, "");
36781         }
36782         
36783         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36784         return isNaN(value) ? '' : value;
36785     },
36786
36787     fixPrecision : function(value)
36788     {
36789         if(this.thousandsDelimiter) {
36790             value += "";
36791             r = new RegExp(",", "g");
36792             value = value.replace(r, "");
36793         }
36794         
36795         var nan = isNaN(value);
36796         
36797         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36798             return nan ? '' : value;
36799         }
36800         return parseFloat(value).toFixed(this.decimalPrecision);
36801     },
36802
36803     setValue : function(v)
36804     {
36805         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36806         
36807         this.value = v;
36808         
36809         if(this.rendered){
36810             
36811             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36812             
36813             this.inputEl().dom.value = (v == '') ? '' :
36814                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36815             
36816             if(!this.allowZero && v === '0') {
36817                 this.hiddenEl().dom.value = '';
36818                 this.inputEl().dom.value = '';
36819             }
36820             
36821             this.validate();
36822         }
36823     },
36824
36825     decimalPrecisionFcn : function(v)
36826     {
36827         return Math.floor(v);
36828     },
36829
36830     beforeBlur : function()
36831     {
36832         var v = this.parseValue(this.getRawValue());
36833         
36834         if(v || v === 0 || v === ''){
36835             this.setValue(v);
36836         }
36837     },
36838     
36839     hiddenEl : function()
36840     {
36841         return this.el.select('input.hidden-number-input',true).first();
36842     }
36843     
36844 });
36845
36846  
36847
36848 /*
36849 * Licence: LGPL
36850 */
36851
36852 /**
36853  * @class Roo.bootstrap.DocumentSlider
36854  * @extends Roo.bootstrap.Component
36855  * Bootstrap DocumentSlider class
36856  * 
36857  * @constructor
36858  * Create a new DocumentViewer
36859  * @param {Object} config The config object
36860  */
36861
36862 Roo.bootstrap.DocumentSlider = function(config){
36863     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36864     
36865     this.files = [];
36866     
36867     this.addEvents({
36868         /**
36869          * @event initial
36870          * Fire after initEvent
36871          * @param {Roo.bootstrap.DocumentSlider} this
36872          */
36873         "initial" : true,
36874         /**
36875          * @event update
36876          * Fire after update
36877          * @param {Roo.bootstrap.DocumentSlider} this
36878          */
36879         "update" : true,
36880         /**
36881          * @event click
36882          * Fire after click
36883          * @param {Roo.bootstrap.DocumentSlider} this
36884          */
36885         "click" : true
36886     });
36887 };
36888
36889 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36890     
36891     files : false,
36892     
36893     indicator : 0,
36894     
36895     getAutoCreate : function()
36896     {
36897         var cfg = {
36898             tag : 'div',
36899             cls : 'roo-document-slider',
36900             cn : [
36901                 {
36902                     tag : 'div',
36903                     cls : 'roo-document-slider-header',
36904                     cn : [
36905                         {
36906                             tag : 'div',
36907                             cls : 'roo-document-slider-header-title'
36908                         }
36909                     ]
36910                 },
36911                 {
36912                     tag : 'div',
36913                     cls : 'roo-document-slider-body',
36914                     cn : [
36915                         {
36916                             tag : 'div',
36917                             cls : 'roo-document-slider-prev',
36918                             cn : [
36919                                 {
36920                                     tag : 'i',
36921                                     cls : 'fa fa-chevron-left'
36922                                 }
36923                             ]
36924                         },
36925                         {
36926                             tag : 'div',
36927                             cls : 'roo-document-slider-thumb',
36928                             cn : [
36929                                 {
36930                                     tag : 'img',
36931                                     cls : 'roo-document-slider-image'
36932                                 }
36933                             ]
36934                         },
36935                         {
36936                             tag : 'div',
36937                             cls : 'roo-document-slider-next',
36938                             cn : [
36939                                 {
36940                                     tag : 'i',
36941                                     cls : 'fa fa-chevron-right'
36942                                 }
36943                             ]
36944                         }
36945                     ]
36946                 }
36947             ]
36948         };
36949         
36950         return cfg;
36951     },
36952     
36953     initEvents : function()
36954     {
36955         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36956         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36957         
36958         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36959         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36960         
36961         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36962         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36963         
36964         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36965         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36966         
36967         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36968         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36969         
36970         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36971         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36972         
36973         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36974         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36975         
36976         this.thumbEl.on('click', this.onClick, this);
36977         
36978         this.prevIndicator.on('click', this.prev, this);
36979         
36980         this.nextIndicator.on('click', this.next, this);
36981         
36982     },
36983     
36984     initial : function()
36985     {
36986         if(this.files.length){
36987             this.indicator = 1;
36988             this.update()
36989         }
36990         
36991         this.fireEvent('initial', this);
36992     },
36993     
36994     update : function()
36995     {
36996         this.imageEl.attr('src', this.files[this.indicator - 1]);
36997         
36998         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36999         
37000         this.prevIndicator.show();
37001         
37002         if(this.indicator == 1){
37003             this.prevIndicator.hide();
37004         }
37005         
37006         this.nextIndicator.show();
37007         
37008         if(this.indicator == this.files.length){
37009             this.nextIndicator.hide();
37010         }
37011         
37012         this.thumbEl.scrollTo('top');
37013         
37014         this.fireEvent('update', this);
37015     },
37016     
37017     onClick : function(e)
37018     {
37019         e.preventDefault();
37020         
37021         this.fireEvent('click', this);
37022     },
37023     
37024     prev : function(e)
37025     {
37026         e.preventDefault();
37027         
37028         this.indicator = Math.max(1, this.indicator - 1);
37029         
37030         this.update();
37031     },
37032     
37033     next : function(e)
37034     {
37035         e.preventDefault();
37036         
37037         this.indicator = Math.min(this.files.length, this.indicator + 1);
37038         
37039         this.update();
37040     }
37041 });
37042 /*
37043  * - LGPL
37044  *
37045  * RadioSet
37046  *
37047  *
37048  */
37049
37050 /**
37051  * @class Roo.bootstrap.form.RadioSet
37052  * @extends Roo.bootstrap.form.Input
37053  * @children Roo.bootstrap.form.Radio
37054  * Bootstrap RadioSet class
37055  * @cfg {String} indicatorpos (left|right) default left
37056  * @cfg {Boolean} inline (true|false) inline the element (default true)
37057  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37058  * @constructor
37059  * Create a new RadioSet
37060  * @param {Object} config The config object
37061  */
37062
37063 Roo.bootstrap.form.RadioSet = function(config){
37064     
37065     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37066     
37067     this.radioes = [];
37068     
37069     Roo.bootstrap.form.RadioSet.register(this);
37070     
37071     this.addEvents({
37072         /**
37073         * @event check
37074         * Fires when the element is checked or unchecked.
37075         * @param {Roo.bootstrap.form.RadioSet} this This radio
37076         * @param {Roo.bootstrap.form.Radio} item The checked item
37077         */
37078        check : true,
37079        /**
37080         * @event click
37081         * Fires when the element is click.
37082         * @param {Roo.bootstrap.form.RadioSet} this This radio set
37083         * @param {Roo.bootstrap.form.Radio} item The checked item
37084         * @param {Roo.EventObject} e The event object
37085         */
37086        click : true
37087     });
37088     
37089 };
37090
37091 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
37092
37093     radioes : false,
37094     
37095     inline : true,
37096     
37097     weight : '',
37098     
37099     indicatorpos : 'left',
37100     
37101     getAutoCreate : function()
37102     {
37103         var label = {
37104             tag : 'label',
37105             cls : 'roo-radio-set-label',
37106             cn : [
37107                 {
37108                     tag : 'span',
37109                     html : this.fieldLabel
37110                 }
37111             ]
37112         };
37113         if (Roo.bootstrap.version == 3) {
37114             
37115             
37116             if(this.indicatorpos == 'left'){
37117                 label.cn.unshift({
37118                     tag : 'i',
37119                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37120                     tooltip : 'This field is required'
37121                 });
37122             } else {
37123                 label.cn.push({
37124                     tag : 'i',
37125                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37126                     tooltip : 'This field is required'
37127                 });
37128             }
37129         }
37130         var items = {
37131             tag : 'div',
37132             cls : 'roo-radio-set-items'
37133         };
37134         
37135         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37136         
37137         if (align === 'left' && this.fieldLabel.length) {
37138             
37139             items = {
37140                 cls : "roo-radio-set-right", 
37141                 cn: [
37142                     items
37143                 ]
37144             };
37145             
37146             if(this.labelWidth > 12){
37147                 label.style = "width: " + this.labelWidth + 'px';
37148             }
37149             
37150             if(this.labelWidth < 13 && this.labelmd == 0){
37151                 this.labelmd = this.labelWidth;
37152             }
37153             
37154             if(this.labellg > 0){
37155                 label.cls += ' col-lg-' + this.labellg;
37156                 items.cls += ' col-lg-' + (12 - this.labellg);
37157             }
37158             
37159             if(this.labelmd > 0){
37160                 label.cls += ' col-md-' + this.labelmd;
37161                 items.cls += ' col-md-' + (12 - this.labelmd);
37162             }
37163             
37164             if(this.labelsm > 0){
37165                 label.cls += ' col-sm-' + this.labelsm;
37166                 items.cls += ' col-sm-' + (12 - this.labelsm);
37167             }
37168             
37169             if(this.labelxs > 0){
37170                 label.cls += ' col-xs-' + this.labelxs;
37171                 items.cls += ' col-xs-' + (12 - this.labelxs);
37172             }
37173         }
37174         
37175         var cfg = {
37176             tag : 'div',
37177             cls : 'roo-radio-set',
37178             cn : [
37179                 {
37180                     tag : 'input',
37181                     cls : 'roo-radio-set-input',
37182                     type : 'hidden',
37183                     name : this.name,
37184                     value : this.value ? this.value :  ''
37185                 },
37186                 label,
37187                 items
37188             ]
37189         };
37190         
37191         if(this.weight.length){
37192             cfg.cls += ' roo-radio-' + this.weight;
37193         }
37194         
37195         if(this.inline) {
37196             cfg.cls += ' roo-radio-set-inline';
37197         }
37198         
37199         var settings=this;
37200         ['xs','sm','md','lg'].map(function(size){
37201             if (settings[size]) {
37202                 cfg.cls += ' col-' + size + '-' + settings[size];
37203             }
37204         });
37205         
37206         return cfg;
37207         
37208     },
37209
37210     initEvents : function()
37211     {
37212         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37213         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37214         
37215         if(!this.fieldLabel.length){
37216             this.labelEl.hide();
37217         }
37218         
37219         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37220         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37221         
37222         this.indicator = this.indicatorEl();
37223         
37224         if(this.indicator){
37225             this.indicator.addClass('invisible');
37226         }
37227         
37228         this.originalValue = this.getValue();
37229         
37230     },
37231     
37232     inputEl: function ()
37233     {
37234         return this.el.select('.roo-radio-set-input', true).first();
37235     },
37236     
37237     getChildContainer : function()
37238     {
37239         return this.itemsEl;
37240     },
37241     
37242     register : function(item)
37243     {
37244         this.radioes.push(item);
37245         
37246     },
37247     
37248     validate : function()
37249     {   
37250         if(this.getVisibilityEl().hasClass('hidden')){
37251             return true;
37252         }
37253         
37254         var valid = false;
37255         
37256         Roo.each(this.radioes, function(i){
37257             if(!i.checked){
37258                 return;
37259             }
37260             
37261             valid = true;
37262             return false;
37263         });
37264         
37265         if(this.allowBlank) {
37266             return true;
37267         }
37268         
37269         if(this.disabled || valid){
37270             this.markValid();
37271             return true;
37272         }
37273         
37274         this.markInvalid();
37275         return false;
37276         
37277     },
37278     
37279     markValid : function()
37280     {
37281         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37282             this.indicatorEl().removeClass('visible');
37283             this.indicatorEl().addClass('invisible');
37284         }
37285         
37286         
37287         if (Roo.bootstrap.version == 3) {
37288             this.el.removeClass([this.invalidClass, this.validClass]);
37289             this.el.addClass(this.validClass);
37290         } else {
37291             this.el.removeClass(['is-invalid','is-valid']);
37292             this.el.addClass(['is-valid']);
37293         }
37294         this.fireEvent('valid', this);
37295     },
37296     
37297     markInvalid : function(msg)
37298     {
37299         if(this.allowBlank || this.disabled){
37300             return;
37301         }
37302         
37303         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37304             this.indicatorEl().removeClass('invisible');
37305             this.indicatorEl().addClass('visible');
37306         }
37307         if (Roo.bootstrap.version == 3) {
37308             this.el.removeClass([this.invalidClass, this.validClass]);
37309             this.el.addClass(this.invalidClass);
37310         } else {
37311             this.el.removeClass(['is-invalid','is-valid']);
37312             this.el.addClass(['is-invalid']);
37313         }
37314         
37315         this.fireEvent('invalid', this, msg);
37316         
37317     },
37318     
37319     setValue : function(v, suppressEvent)
37320     {   
37321         if(this.value === v){
37322             return;
37323         }
37324         
37325         this.value = v;
37326         
37327         if(this.rendered){
37328             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37329         }
37330         
37331         Roo.each(this.radioes, function(i){
37332             i.checked = false;
37333             i.el.removeClass('checked');
37334         });
37335         
37336         Roo.each(this.radioes, function(i){
37337             
37338             if(i.value === v || i.value.toString() === v.toString()){
37339                 i.checked = true;
37340                 i.el.addClass('checked');
37341                 
37342                 if(suppressEvent !== true){
37343                     this.fireEvent('check', this, i);
37344                 }
37345                 
37346                 return false;
37347             }
37348             
37349         }, this);
37350         
37351         this.validate();
37352     },
37353     
37354     clearInvalid : function(){
37355         
37356         if(!this.el || this.preventMark){
37357             return;
37358         }
37359         
37360         this.el.removeClass([this.invalidClass]);
37361         
37362         this.fireEvent('valid', this);
37363     }
37364     
37365 });
37366
37367 Roo.apply(Roo.bootstrap.form.RadioSet, {
37368     
37369     groups: {},
37370     
37371     register : function(set)
37372     {
37373         this.groups[set.name] = set;
37374     },
37375     
37376     get: function(name) 
37377     {
37378         if (typeof(this.groups[name]) == 'undefined') {
37379             return false;
37380         }
37381         
37382         return this.groups[name] ;
37383     }
37384     
37385 });
37386 /*
37387  * Based on:
37388  * Ext JS Library 1.1.1
37389  * Copyright(c) 2006-2007, Ext JS, LLC.
37390  *
37391  * Originally Released Under LGPL - original licence link has changed is not relivant.
37392  *
37393  * Fork - LGPL
37394  * <script type="text/javascript">
37395  */
37396
37397
37398 /**
37399  * @class Roo.bootstrap.SplitBar
37400  * @extends Roo.util.Observable
37401  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37402  * <br><br>
37403  * Usage:
37404  * <pre><code>
37405 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37406                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37407 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37408 split.minSize = 100;
37409 split.maxSize = 600;
37410 split.animate = true;
37411 split.on('moved', splitterMoved);
37412 </code></pre>
37413  * @constructor
37414  * Create a new SplitBar
37415  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37416  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37417  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37418  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37419                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37420                         position of the SplitBar).
37421  */
37422 Roo.bootstrap.SplitBar = function(cfg){
37423     
37424     /** @private */
37425     
37426     //{
37427     //  dragElement : elm
37428     //  resizingElement: el,
37429         // optional..
37430     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37431     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37432         // existingProxy ???
37433     //}
37434     
37435     this.el = Roo.get(cfg.dragElement, true);
37436     this.el.dom.unselectable = "on";
37437     /** @private */
37438     this.resizingEl = Roo.get(cfg.resizingElement, true);
37439
37440     /**
37441      * @private
37442      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37443      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37444      * @type Number
37445      */
37446     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37447     
37448     /**
37449      * The minimum size of the resizing element. (Defaults to 0)
37450      * @type Number
37451      */
37452     this.minSize = 0;
37453     
37454     /**
37455      * The maximum size of the resizing element. (Defaults to 2000)
37456      * @type Number
37457      */
37458     this.maxSize = 2000;
37459     
37460     /**
37461      * Whether to animate the transition to the new size
37462      * @type Boolean
37463      */
37464     this.animate = false;
37465     
37466     /**
37467      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37468      * @type Boolean
37469      */
37470     this.useShim = false;
37471     
37472     /** @private */
37473     this.shim = null;
37474     
37475     if(!cfg.existingProxy){
37476         /** @private */
37477         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37478     }else{
37479         this.proxy = Roo.get(cfg.existingProxy).dom;
37480     }
37481     /** @private */
37482     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37483     
37484     /** @private */
37485     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37486     
37487     /** @private */
37488     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37489     
37490     /** @private */
37491     this.dragSpecs = {};
37492     
37493     /**
37494      * @private The adapter to use to positon and resize elements
37495      */
37496     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37497     this.adapter.init(this);
37498     
37499     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37500         /** @private */
37501         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37502         this.el.addClass("roo-splitbar-h");
37503     }else{
37504         /** @private */
37505         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37506         this.el.addClass("roo-splitbar-v");
37507     }
37508     
37509     this.addEvents({
37510         /**
37511          * @event resize
37512          * Fires when the splitter is moved (alias for {@link #event-moved})
37513          * @param {Roo.bootstrap.SplitBar} this
37514          * @param {Number} newSize the new width or height
37515          */
37516         "resize" : true,
37517         /**
37518          * @event moved
37519          * Fires when the splitter is moved
37520          * @param {Roo.bootstrap.SplitBar} this
37521          * @param {Number} newSize the new width or height
37522          */
37523         "moved" : true,
37524         /**
37525          * @event beforeresize
37526          * Fires before the splitter is dragged
37527          * @param {Roo.bootstrap.SplitBar} this
37528          */
37529         "beforeresize" : true,
37530
37531         "beforeapply" : true
37532     });
37533
37534     Roo.util.Observable.call(this);
37535 };
37536
37537 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37538     onStartProxyDrag : function(x, y){
37539         this.fireEvent("beforeresize", this);
37540         if(!this.overlay){
37541             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37542             o.unselectable();
37543             o.enableDisplayMode("block");
37544             // all splitbars share the same overlay
37545             Roo.bootstrap.SplitBar.prototype.overlay = o;
37546         }
37547         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37548         this.overlay.show();
37549         Roo.get(this.proxy).setDisplayed("block");
37550         var size = this.adapter.getElementSize(this);
37551         this.activeMinSize = this.getMinimumSize();;
37552         this.activeMaxSize = this.getMaximumSize();;
37553         var c1 = size - this.activeMinSize;
37554         var c2 = Math.max(this.activeMaxSize - size, 0);
37555         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37556             this.dd.resetConstraints();
37557             this.dd.setXConstraint(
37558                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37559                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37560             );
37561             this.dd.setYConstraint(0, 0);
37562         }else{
37563             this.dd.resetConstraints();
37564             this.dd.setXConstraint(0, 0);
37565             this.dd.setYConstraint(
37566                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37567                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37568             );
37569          }
37570         this.dragSpecs.startSize = size;
37571         this.dragSpecs.startPoint = [x, y];
37572         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37573     },
37574     
37575     /** 
37576      * @private Called after the drag operation by the DDProxy
37577      */
37578     onEndProxyDrag : function(e){
37579         Roo.get(this.proxy).setDisplayed(false);
37580         var endPoint = Roo.lib.Event.getXY(e);
37581         if(this.overlay){
37582             this.overlay.hide();
37583         }
37584         var newSize;
37585         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37586             newSize = this.dragSpecs.startSize + 
37587                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37588                     endPoint[0] - this.dragSpecs.startPoint[0] :
37589                     this.dragSpecs.startPoint[0] - endPoint[0]
37590                 );
37591         }else{
37592             newSize = this.dragSpecs.startSize + 
37593                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37594                     endPoint[1] - this.dragSpecs.startPoint[1] :
37595                     this.dragSpecs.startPoint[1] - endPoint[1]
37596                 );
37597         }
37598         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37599         if(newSize != this.dragSpecs.startSize){
37600             if(this.fireEvent('beforeapply', this, newSize) !== false){
37601                 this.adapter.setElementSize(this, newSize);
37602                 this.fireEvent("moved", this, newSize);
37603                 this.fireEvent("resize", this, newSize);
37604             }
37605         }
37606     },
37607     
37608     /**
37609      * Get the adapter this SplitBar uses
37610      * @return The adapter object
37611      */
37612     getAdapter : function(){
37613         return this.adapter;
37614     },
37615     
37616     /**
37617      * Set the adapter this SplitBar uses
37618      * @param {Object} adapter A SplitBar adapter object
37619      */
37620     setAdapter : function(adapter){
37621         this.adapter = adapter;
37622         this.adapter.init(this);
37623     },
37624     
37625     /**
37626      * Gets the minimum size for the resizing element
37627      * @return {Number} The minimum size
37628      */
37629     getMinimumSize : function(){
37630         return this.minSize;
37631     },
37632     
37633     /**
37634      * Sets the minimum size for the resizing element
37635      * @param {Number} minSize The minimum size
37636      */
37637     setMinimumSize : function(minSize){
37638         this.minSize = minSize;
37639     },
37640     
37641     /**
37642      * Gets the maximum size for the resizing element
37643      * @return {Number} The maximum size
37644      */
37645     getMaximumSize : function(){
37646         return this.maxSize;
37647     },
37648     
37649     /**
37650      * Sets the maximum size for the resizing element
37651      * @param {Number} maxSize The maximum size
37652      */
37653     setMaximumSize : function(maxSize){
37654         this.maxSize = maxSize;
37655     },
37656     
37657     /**
37658      * Sets the initialize size for the resizing element
37659      * @param {Number} size The initial size
37660      */
37661     setCurrentSize : function(size){
37662         var oldAnimate = this.animate;
37663         this.animate = false;
37664         this.adapter.setElementSize(this, size);
37665         this.animate = oldAnimate;
37666     },
37667     
37668     /**
37669      * Destroy this splitbar. 
37670      * @param {Boolean} removeEl True to remove the element
37671      */
37672     destroy : function(removeEl){
37673         if(this.shim){
37674             this.shim.remove();
37675         }
37676         this.dd.unreg();
37677         this.proxy.parentNode.removeChild(this.proxy);
37678         if(removeEl){
37679             this.el.remove();
37680         }
37681     }
37682 });
37683
37684 /**
37685  * @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.
37686  */
37687 Roo.bootstrap.SplitBar.createProxy = function(dir){
37688     var proxy = new Roo.Element(document.createElement("div"));
37689     proxy.unselectable();
37690     var cls = 'roo-splitbar-proxy';
37691     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37692     document.body.appendChild(proxy.dom);
37693     return proxy.dom;
37694 };
37695
37696 /** 
37697  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37698  * Default Adapter. It assumes the splitter and resizing element are not positioned
37699  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37700  */
37701 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37702 };
37703
37704 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37705     // do nothing for now
37706     init : function(s){
37707     
37708     },
37709     /**
37710      * Called before drag operations to get the current size of the resizing element. 
37711      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37712      */
37713      getElementSize : function(s){
37714         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37715             return s.resizingEl.getWidth();
37716         }else{
37717             return s.resizingEl.getHeight();
37718         }
37719     },
37720     
37721     /**
37722      * Called after drag operations to set the size of the resizing element.
37723      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37724      * @param {Number} newSize The new size to set
37725      * @param {Function} onComplete A function to be invoked when resizing is complete
37726      */
37727     setElementSize : function(s, newSize, onComplete){
37728         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37729             if(!s.animate){
37730                 s.resizingEl.setWidth(newSize);
37731                 if(onComplete){
37732                     onComplete(s, newSize);
37733                 }
37734             }else{
37735                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37736             }
37737         }else{
37738             
37739             if(!s.animate){
37740                 s.resizingEl.setHeight(newSize);
37741                 if(onComplete){
37742                     onComplete(s, newSize);
37743                 }
37744             }else{
37745                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37746             }
37747         }
37748     }
37749 };
37750
37751 /** 
37752  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37753  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37754  * Adapter that  moves the splitter element to align with the resized sizing element. 
37755  * Used with an absolute positioned SplitBar.
37756  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37757  * document.body, make sure you assign an id to the body element.
37758  */
37759 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37760     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37761     this.container = Roo.get(container);
37762 };
37763
37764 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37765     init : function(s){
37766         this.basic.init(s);
37767     },
37768     
37769     getElementSize : function(s){
37770         return this.basic.getElementSize(s);
37771     },
37772     
37773     setElementSize : function(s, newSize, onComplete){
37774         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37775     },
37776     
37777     moveSplitter : function(s){
37778         var yes = Roo.bootstrap.SplitBar;
37779         switch(s.placement){
37780             case yes.LEFT:
37781                 s.el.setX(s.resizingEl.getRight());
37782                 break;
37783             case yes.RIGHT:
37784                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37785                 break;
37786             case yes.TOP:
37787                 s.el.setY(s.resizingEl.getBottom());
37788                 break;
37789             case yes.BOTTOM:
37790                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37791                 break;
37792         }
37793     }
37794 };
37795
37796 /**
37797  * Orientation constant - Create a vertical SplitBar
37798  * @static
37799  * @type Number
37800  */
37801 Roo.bootstrap.SplitBar.VERTICAL = 1;
37802
37803 /**
37804  * Orientation constant - Create a horizontal SplitBar
37805  * @static
37806  * @type Number
37807  */
37808 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37809
37810 /**
37811  * Placement constant - The resizing element is to the left of the splitter element
37812  * @static
37813  * @type Number
37814  */
37815 Roo.bootstrap.SplitBar.LEFT = 1;
37816
37817 /**
37818  * Placement constant - The resizing element is to the right of the splitter element
37819  * @static
37820  * @type Number
37821  */
37822 Roo.bootstrap.SplitBar.RIGHT = 2;
37823
37824 /**
37825  * Placement constant - The resizing element is positioned above the splitter element
37826  * @static
37827  * @type Number
37828  */
37829 Roo.bootstrap.SplitBar.TOP = 3;
37830
37831 /**
37832  * Placement constant - The resizing element is positioned under splitter element
37833  * @static
37834  * @type Number
37835  */
37836 Roo.bootstrap.SplitBar.BOTTOM = 4;
37837 /*
37838  * Based on:
37839  * Ext JS Library 1.1.1
37840  * Copyright(c) 2006-2007, Ext JS, LLC.
37841  *
37842  * Originally Released Under LGPL - original licence link has changed is not relivant.
37843  *
37844  * Fork - LGPL
37845  * <script type="text/javascript">
37846  */
37847
37848 /**
37849  * @class Roo.bootstrap.layout.Manager
37850  * @extends Roo.bootstrap.Component
37851  * @abstract
37852  * Base class for layout managers.
37853  */
37854 Roo.bootstrap.layout.Manager = function(config)
37855 {
37856     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37857
37858
37859
37860
37861
37862     /** false to disable window resize monitoring @type Boolean */
37863     this.monitorWindowResize = true;
37864     this.regions = {};
37865     this.addEvents({
37866         /**
37867          * @event layout
37868          * Fires when a layout is performed.
37869          * @param {Roo.LayoutManager} this
37870          */
37871         "layout" : true,
37872         /**
37873          * @event regionresized
37874          * Fires when the user resizes a region.
37875          * @param {Roo.LayoutRegion} region The resized region
37876          * @param {Number} newSize The new size (width for east/west, height for north/south)
37877          */
37878         "regionresized" : true,
37879         /**
37880          * @event regioncollapsed
37881          * Fires when a region is collapsed.
37882          * @param {Roo.LayoutRegion} region The collapsed region
37883          */
37884         "regioncollapsed" : true,
37885         /**
37886          * @event regionexpanded
37887          * Fires when a region is expanded.
37888          * @param {Roo.LayoutRegion} region The expanded region
37889          */
37890         "regionexpanded" : true
37891     });
37892     this.updating = false;
37893
37894     if (config.el) {
37895         this.el = Roo.get(config.el);
37896         this.initEvents();
37897     }
37898
37899 };
37900
37901 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37902
37903
37904     regions : null,
37905
37906     monitorWindowResize : true,
37907
37908
37909     updating : false,
37910
37911
37912     onRender : function(ct, position)
37913     {
37914         if(!this.el){
37915             this.el = Roo.get(ct);
37916             this.initEvents();
37917         }
37918         //this.fireEvent('render',this);
37919     },
37920
37921
37922     initEvents: function()
37923     {
37924
37925
37926         // ie scrollbar fix
37927         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37928             document.body.scroll = "no";
37929         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37930             this.el.position('relative');
37931         }
37932         this.id = this.el.id;
37933         this.el.addClass("roo-layout-container");
37934         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37935         if(this.el.dom != document.body ) {
37936             this.el.on('resize', this.layout,this);
37937             this.el.on('show', this.layout,this);
37938         }
37939
37940     },
37941
37942     /**
37943      * Returns true if this layout is currently being updated
37944      * @return {Boolean}
37945      */
37946     isUpdating : function(){
37947         return this.updating;
37948     },
37949
37950     /**
37951      * Suspend the LayoutManager from doing auto-layouts while
37952      * making multiple add or remove calls
37953      */
37954     beginUpdate : function(){
37955         this.updating = true;
37956     },
37957
37958     /**
37959      * Restore auto-layouts and optionally disable the manager from performing a layout
37960      * @param {Boolean} noLayout true to disable a layout update
37961      */
37962     endUpdate : function(noLayout){
37963         this.updating = false;
37964         if(!noLayout){
37965             this.layout();
37966         }
37967     },
37968
37969     layout: function(){
37970         // abstract...
37971     },
37972
37973     onRegionResized : function(region, newSize){
37974         this.fireEvent("regionresized", region, newSize);
37975         this.layout();
37976     },
37977
37978     onRegionCollapsed : function(region){
37979         this.fireEvent("regioncollapsed", region);
37980     },
37981
37982     onRegionExpanded : function(region){
37983         this.fireEvent("regionexpanded", region);
37984     },
37985
37986     /**
37987      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37988      * performs box-model adjustments.
37989      * @return {Object} The size as an object {width: (the width), height: (the height)}
37990      */
37991     getViewSize : function()
37992     {
37993         var size;
37994         if(this.el.dom != document.body){
37995             size = this.el.getSize();
37996         }else{
37997             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37998         }
37999         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38000         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38001         return size;
38002     },
38003
38004     /**
38005      * Returns the Element this layout is bound to.
38006      * @return {Roo.Element}
38007      */
38008     getEl : function(){
38009         return this.el;
38010     },
38011
38012     /**
38013      * Returns the specified region.
38014      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38015      * @return {Roo.LayoutRegion}
38016      */
38017     getRegion : function(target){
38018         return this.regions[target.toLowerCase()];
38019     },
38020
38021     onWindowResize : function(){
38022         if(this.monitorWindowResize){
38023             this.layout();
38024         }
38025     }
38026 });
38027 /*
38028  * Based on:
38029  * Ext JS Library 1.1.1
38030  * Copyright(c) 2006-2007, Ext JS, LLC.
38031  *
38032  * Originally Released Under LGPL - original licence link has changed is not relivant.
38033  *
38034  * Fork - LGPL
38035  * <script type="text/javascript">
38036  */
38037 /**
38038  * @class Roo.bootstrap.layout.Border
38039  * @extends Roo.bootstrap.layout.Manager
38040  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38041  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
38042  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38043  * please see: examples/bootstrap/nested.html<br><br>
38044  
38045 <b>The container the layout is rendered into can be either the body element or any other element.
38046 If it is not the body element, the container needs to either be an absolute positioned element,
38047 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38048 the container size if it is not the body element.</b>
38049
38050 * @constructor
38051 * Create a new Border
38052 * @param {Object} config Configuration options
38053  */
38054 Roo.bootstrap.layout.Border = function(config){
38055     config = config || {};
38056     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38057     
38058     
38059     
38060     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38061         if(config[region]){
38062             config[region].region = region;
38063             this.addRegion(config[region]);
38064         }
38065     },this);
38066     
38067 };
38068
38069 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38070
38071 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38072     
38073         /**
38074          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38075          */
38076         /**
38077          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38078          */
38079         /**
38080          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38081          */
38082         /**
38083          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38084          */
38085         /**
38086          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38087          */
38088         
38089         
38090         
38091         
38092     parent : false, // this might point to a 'nest' or a ???
38093     
38094     /**
38095      * Creates and adds a new region if it doesn't already exist.
38096      * @param {String} target The target region key (north, south, east, west or center).
38097      * @param {Object} config The regions config object
38098      * @return {BorderLayoutRegion} The new region
38099      */
38100     addRegion : function(config)
38101     {
38102         if(!this.regions[config.region]){
38103             var r = this.factory(config);
38104             this.bindRegion(r);
38105         }
38106         return this.regions[config.region];
38107     },
38108
38109     // private (kinda)
38110     bindRegion : function(r){
38111         this.regions[r.config.region] = r;
38112         
38113         r.on("visibilitychange",    this.layout, this);
38114         r.on("paneladded",          this.layout, this);
38115         r.on("panelremoved",        this.layout, this);
38116         r.on("invalidated",         this.layout, this);
38117         r.on("resized",             this.onRegionResized, this);
38118         r.on("collapsed",           this.onRegionCollapsed, this);
38119         r.on("expanded",            this.onRegionExpanded, this);
38120     },
38121
38122     /**
38123      * Performs a layout update.
38124      */
38125     layout : function()
38126     {
38127         if(this.updating) {
38128             return;
38129         }
38130         
38131         // render all the rebions if they have not been done alreayd?
38132         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38133             if(this.regions[region] && !this.regions[region].bodyEl){
38134                 this.regions[region].onRender(this.el)
38135             }
38136         },this);
38137         
38138         var size = this.getViewSize();
38139         var w = size.width;
38140         var h = size.height;
38141         var centerW = w;
38142         var centerH = h;
38143         var centerY = 0;
38144         var centerX = 0;
38145         //var x = 0, y = 0;
38146
38147         var rs = this.regions;
38148         var north = rs["north"];
38149         var south = rs["south"]; 
38150         var west = rs["west"];
38151         var east = rs["east"];
38152         var center = rs["center"];
38153         //if(this.hideOnLayout){ // not supported anymore
38154             //c.el.setStyle("display", "none");
38155         //}
38156         if(north && north.isVisible()){
38157             var b = north.getBox();
38158             var m = north.getMargins();
38159             b.width = w - (m.left+m.right);
38160             b.x = m.left;
38161             b.y = m.top;
38162             centerY = b.height + b.y + m.bottom;
38163             centerH -= centerY;
38164             north.updateBox(this.safeBox(b));
38165         }
38166         if(south && south.isVisible()){
38167             var b = south.getBox();
38168             var m = south.getMargins();
38169             b.width = w - (m.left+m.right);
38170             b.x = m.left;
38171             var totalHeight = (b.height + m.top + m.bottom);
38172             b.y = h - totalHeight + m.top;
38173             centerH -= totalHeight;
38174             south.updateBox(this.safeBox(b));
38175         }
38176         if(west && west.isVisible()){
38177             var b = west.getBox();
38178             var m = west.getMargins();
38179             b.height = centerH - (m.top+m.bottom);
38180             b.x = m.left;
38181             b.y = centerY + m.top;
38182             var totalWidth = (b.width + m.left + m.right);
38183             centerX += totalWidth;
38184             centerW -= totalWidth;
38185             west.updateBox(this.safeBox(b));
38186         }
38187         if(east && east.isVisible()){
38188             var b = east.getBox();
38189             var m = east.getMargins();
38190             b.height = centerH - (m.top+m.bottom);
38191             var totalWidth = (b.width + m.left + m.right);
38192             b.x = w - totalWidth + m.left;
38193             b.y = centerY + m.top;
38194             centerW -= totalWidth;
38195             east.updateBox(this.safeBox(b));
38196         }
38197         if(center){
38198             var m = center.getMargins();
38199             var centerBox = {
38200                 x: centerX + m.left,
38201                 y: centerY + m.top,
38202                 width: centerW - (m.left+m.right),
38203                 height: centerH - (m.top+m.bottom)
38204             };
38205             //if(this.hideOnLayout){
38206                 //center.el.setStyle("display", "block");
38207             //}
38208             center.updateBox(this.safeBox(centerBox));
38209         }
38210         this.el.repaint();
38211         this.fireEvent("layout", this);
38212     },
38213
38214     // private
38215     safeBox : function(box){
38216         box.width = Math.max(0, box.width);
38217         box.height = Math.max(0, box.height);
38218         return box;
38219     },
38220
38221     /**
38222      * Adds a ContentPanel (or subclass) to this layout.
38223      * @param {String} target The target region key (north, south, east, west or center).
38224      * @param {Roo.ContentPanel} panel The panel to add
38225      * @return {Roo.ContentPanel} The added panel
38226      */
38227     add : function(target, panel){
38228          
38229         target = target.toLowerCase();
38230         return this.regions[target].add(panel);
38231     },
38232
38233     /**
38234      * Remove a ContentPanel (or subclass) to this layout.
38235      * @param {String} target The target region key (north, south, east, west or center).
38236      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38237      * @return {Roo.ContentPanel} The removed panel
38238      */
38239     remove : function(target, panel){
38240         target = target.toLowerCase();
38241         return this.regions[target].remove(panel);
38242     },
38243
38244     /**
38245      * Searches all regions for a panel with the specified id
38246      * @param {String} panelId
38247      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38248      */
38249     findPanel : function(panelId){
38250         var rs = this.regions;
38251         for(var target in rs){
38252             if(typeof rs[target] != "function"){
38253                 var p = rs[target].getPanel(panelId);
38254                 if(p){
38255                     return p;
38256                 }
38257             }
38258         }
38259         return null;
38260     },
38261
38262     /**
38263      * Searches all regions for a panel with the specified id and activates (shows) it.
38264      * @param {String/ContentPanel} panelId The panels id or the panel itself
38265      * @return {Roo.ContentPanel} The shown panel or null
38266      */
38267     showPanel : function(panelId) {
38268       var rs = this.regions;
38269       for(var target in rs){
38270          var r = rs[target];
38271          if(typeof r != "function"){
38272             if(r.hasPanel(panelId)){
38273                return r.showPanel(panelId);
38274             }
38275          }
38276       }
38277       return null;
38278    },
38279
38280    /**
38281      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38282      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38283      */
38284    /*
38285     restoreState : function(provider){
38286         if(!provider){
38287             provider = Roo.state.Manager;
38288         }
38289         var sm = new Roo.LayoutStateManager();
38290         sm.init(this, provider);
38291     },
38292 */
38293  
38294  
38295     /**
38296      * Adds a xtype elements to the layout.
38297      * <pre><code>
38298
38299 layout.addxtype({
38300        xtype : 'ContentPanel',
38301        region: 'west',
38302        items: [ .... ]
38303    }
38304 );
38305
38306 layout.addxtype({
38307         xtype : 'NestedLayoutPanel',
38308         region: 'west',
38309         layout: {
38310            center: { },
38311            west: { }   
38312         },
38313         items : [ ... list of content panels or nested layout panels.. ]
38314    }
38315 );
38316 </code></pre>
38317      * @param {Object} cfg Xtype definition of item to add.
38318      */
38319     addxtype : function(cfg)
38320     {
38321         // basically accepts a pannel...
38322         // can accept a layout region..!?!?
38323         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38324         
38325         
38326         // theory?  children can only be panels??
38327         
38328         //if (!cfg.xtype.match(/Panel$/)) {
38329         //    return false;
38330         //}
38331         var ret = false;
38332         
38333         if (typeof(cfg.region) == 'undefined') {
38334             Roo.log("Failed to add Panel, region was not set");
38335             Roo.log(cfg);
38336             return false;
38337         }
38338         var region = cfg.region;
38339         delete cfg.region;
38340         
38341           
38342         var xitems = [];
38343         if (cfg.items) {
38344             xitems = cfg.items;
38345             delete cfg.items;
38346         }
38347         var nb = false;
38348         
38349         if ( region == 'center') {
38350             Roo.log("Center: " + cfg.title);
38351         }
38352         
38353         
38354         switch(cfg.xtype) 
38355         {
38356             case 'Content':  // ContentPanel (el, cfg)
38357             case 'Scroll':  // ContentPanel (el, cfg)
38358             case 'View': 
38359                 cfg.autoCreate = cfg.autoCreate || true;
38360                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38361                 //} else {
38362                 //    var el = this.el.createChild();
38363                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38364                 //}
38365                 
38366                 this.add(region, ret);
38367                 break;
38368             
38369             /*
38370             case 'TreePanel': // our new panel!
38371                 cfg.el = this.el.createChild();
38372                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38373                 this.add(region, ret);
38374                 break;
38375             */
38376             
38377             case 'Nest': 
38378                 // create a new Layout (which is  a Border Layout...
38379                 
38380                 var clayout = cfg.layout;
38381                 clayout.el  = this.el.createChild();
38382                 clayout.items   = clayout.items  || [];
38383                 
38384                 delete cfg.layout;
38385                 
38386                 // replace this exitems with the clayout ones..
38387                 xitems = clayout.items;
38388                  
38389                 // force background off if it's in center...
38390                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38391                     cfg.background = false;
38392                 }
38393                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38394                 
38395                 
38396                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38397                 //console.log('adding nested layout panel '  + cfg.toSource());
38398                 this.add(region, ret);
38399                 nb = {}; /// find first...
38400                 break;
38401             
38402             case 'Grid':
38403                 
38404                 // needs grid and region
38405                 
38406                 //var el = this.getRegion(region).el.createChild();
38407                 /*
38408                  *var el = this.el.createChild();
38409                 // create the grid first...
38410                 cfg.grid.container = el;
38411                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38412                 */
38413                 
38414                 if (region == 'center' && this.active ) {
38415                     cfg.background = false;
38416                 }
38417                 
38418                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38419                 
38420                 this.add(region, ret);
38421                 /*
38422                 if (cfg.background) {
38423                     // render grid on panel activation (if panel background)
38424                     ret.on('activate', function(gp) {
38425                         if (!gp.grid.rendered) {
38426                     //        gp.grid.render(el);
38427                         }
38428                     });
38429                 } else {
38430                   //  cfg.grid.render(el);
38431                 }
38432                 */
38433                 break;
38434            
38435            
38436             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38437                 // it was the old xcomponent building that caused this before.
38438                 // espeically if border is the top element in the tree.
38439                 ret = this;
38440                 break; 
38441                 
38442                     
38443                 
38444                 
38445                 
38446             default:
38447                 /*
38448                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38449                     
38450                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38451                     this.add(region, ret);
38452                 } else {
38453                 */
38454                     Roo.log(cfg);
38455                     throw "Can not add '" + cfg.xtype + "' to Border";
38456                     return null;
38457              
38458                                 
38459              
38460         }
38461         this.beginUpdate();
38462         // add children..
38463         var region = '';
38464         var abn = {};
38465         Roo.each(xitems, function(i)  {
38466             region = nb && i.region ? i.region : false;
38467             
38468             var add = ret.addxtype(i);
38469            
38470             if (region) {
38471                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38472                 if (!i.background) {
38473                     abn[region] = nb[region] ;
38474                 }
38475             }
38476             
38477         });
38478         this.endUpdate();
38479
38480         // make the last non-background panel active..
38481         //if (nb) { Roo.log(abn); }
38482         if (nb) {
38483             
38484             for(var r in abn) {
38485                 region = this.getRegion(r);
38486                 if (region) {
38487                     // tried using nb[r], but it does not work..
38488                      
38489                     region.showPanel(abn[r]);
38490                    
38491                 }
38492             }
38493         }
38494         return ret;
38495         
38496     },
38497     
38498     
38499 // private
38500     factory : function(cfg)
38501     {
38502         
38503         var validRegions = Roo.bootstrap.layout.Border.regions;
38504
38505         var target = cfg.region;
38506         cfg.mgr = this;
38507         
38508         var r = Roo.bootstrap.layout;
38509         Roo.log(target);
38510         switch(target){
38511             case "north":
38512                 return new r.North(cfg);
38513             case "south":
38514                 return new r.South(cfg);
38515             case "east":
38516                 return new r.East(cfg);
38517             case "west":
38518                 return new r.West(cfg);
38519             case "center":
38520                 return new r.Center(cfg);
38521         }
38522         throw 'Layout region "'+target+'" not supported.';
38523     }
38524     
38525     
38526 });
38527  /*
38528  * Based on:
38529  * Ext JS Library 1.1.1
38530  * Copyright(c) 2006-2007, Ext JS, LLC.
38531  *
38532  * Originally Released Under LGPL - original licence link has changed is not relivant.
38533  *
38534  * Fork - LGPL
38535  * <script type="text/javascript">
38536  */
38537  
38538 /**
38539  * @class Roo.bootstrap.layout.Basic
38540  * @extends Roo.util.Observable
38541  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38542  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38543  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38544  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38545  * @cfg {string}   region  the region that it inhabits..
38546  * @cfg {bool}   skipConfig skip config?
38547  * 
38548
38549  */
38550 Roo.bootstrap.layout.Basic = function(config){
38551     
38552     this.mgr = config.mgr;
38553     
38554     this.position = config.region;
38555     
38556     var skipConfig = config.skipConfig;
38557     
38558     this.events = {
38559         /**
38560          * @scope Roo.BasicLayoutRegion
38561          */
38562         
38563         /**
38564          * @event beforeremove
38565          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38566          * @param {Roo.LayoutRegion} this
38567          * @param {Roo.ContentPanel} panel The panel
38568          * @param {Object} e The cancel event object
38569          */
38570         "beforeremove" : true,
38571         /**
38572          * @event invalidated
38573          * Fires when the layout for this region is changed.
38574          * @param {Roo.LayoutRegion} this
38575          */
38576         "invalidated" : true,
38577         /**
38578          * @event visibilitychange
38579          * Fires when this region is shown or hidden 
38580          * @param {Roo.LayoutRegion} this
38581          * @param {Boolean} visibility true or false
38582          */
38583         "visibilitychange" : true,
38584         /**
38585          * @event paneladded
38586          * Fires when a panel is added. 
38587          * @param {Roo.LayoutRegion} this
38588          * @param {Roo.ContentPanel} panel The panel
38589          */
38590         "paneladded" : true,
38591         /**
38592          * @event panelremoved
38593          * Fires when a panel is removed. 
38594          * @param {Roo.LayoutRegion} this
38595          * @param {Roo.ContentPanel} panel The panel
38596          */
38597         "panelremoved" : true,
38598         /**
38599          * @event beforecollapse
38600          * Fires when this region before collapse.
38601          * @param {Roo.LayoutRegion} this
38602          */
38603         "beforecollapse" : true,
38604         /**
38605          * @event collapsed
38606          * Fires when this region is collapsed.
38607          * @param {Roo.LayoutRegion} this
38608          */
38609         "collapsed" : true,
38610         /**
38611          * @event expanded
38612          * Fires when this region is expanded.
38613          * @param {Roo.LayoutRegion} this
38614          */
38615         "expanded" : true,
38616         /**
38617          * @event slideshow
38618          * Fires when this region is slid into view.
38619          * @param {Roo.LayoutRegion} this
38620          */
38621         "slideshow" : true,
38622         /**
38623          * @event slidehide
38624          * Fires when this region slides out of view. 
38625          * @param {Roo.LayoutRegion} this
38626          */
38627         "slidehide" : true,
38628         /**
38629          * @event panelactivated
38630          * Fires when a panel is activated. 
38631          * @param {Roo.LayoutRegion} this
38632          * @param {Roo.ContentPanel} panel The activated panel
38633          */
38634         "panelactivated" : true,
38635         /**
38636          * @event resized
38637          * Fires when the user resizes this region. 
38638          * @param {Roo.LayoutRegion} this
38639          * @param {Number} newSize The new size (width for east/west, height for north/south)
38640          */
38641         "resized" : true
38642     };
38643     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38644     this.panels = new Roo.util.MixedCollection();
38645     this.panels.getKey = this.getPanelId.createDelegate(this);
38646     this.box = null;
38647     this.activePanel = null;
38648     // ensure listeners are added...
38649     
38650     if (config.listeners || config.events) {
38651         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38652             listeners : config.listeners || {},
38653             events : config.events || {}
38654         });
38655     }
38656     
38657     if(skipConfig !== true){
38658         this.applyConfig(config);
38659     }
38660 };
38661
38662 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38663 {
38664     getPanelId : function(p){
38665         return p.getId();
38666     },
38667     
38668     applyConfig : function(config){
38669         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38670         this.config = config;
38671         
38672     },
38673     
38674     /**
38675      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38676      * the width, for horizontal (north, south) the height.
38677      * @param {Number} newSize The new width or height
38678      */
38679     resizeTo : function(newSize){
38680         var el = this.el ? this.el :
38681                  (this.activePanel ? this.activePanel.getEl() : null);
38682         if(el){
38683             switch(this.position){
38684                 case "east":
38685                 case "west":
38686                     el.setWidth(newSize);
38687                     this.fireEvent("resized", this, newSize);
38688                 break;
38689                 case "north":
38690                 case "south":
38691                     el.setHeight(newSize);
38692                     this.fireEvent("resized", this, newSize);
38693                 break;                
38694             }
38695         }
38696     },
38697     
38698     getBox : function(){
38699         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38700     },
38701     
38702     getMargins : function(){
38703         return this.margins;
38704     },
38705     
38706     updateBox : function(box){
38707         this.box = box;
38708         var el = this.activePanel.getEl();
38709         el.dom.style.left = box.x + "px";
38710         el.dom.style.top = box.y + "px";
38711         this.activePanel.setSize(box.width, box.height);
38712     },
38713     
38714     /**
38715      * Returns the container element for this region.
38716      * @return {Roo.Element}
38717      */
38718     getEl : function(){
38719         return this.activePanel;
38720     },
38721     
38722     /**
38723      * Returns true if this region is currently visible.
38724      * @return {Boolean}
38725      */
38726     isVisible : function(){
38727         return this.activePanel ? true : false;
38728     },
38729     
38730     setActivePanel : function(panel){
38731         panel = this.getPanel(panel);
38732         if(this.activePanel && this.activePanel != panel){
38733             this.activePanel.setActiveState(false);
38734             this.activePanel.getEl().setLeftTop(-10000,-10000);
38735         }
38736         this.activePanel = panel;
38737         panel.setActiveState(true);
38738         if(this.box){
38739             panel.setSize(this.box.width, this.box.height);
38740         }
38741         this.fireEvent("panelactivated", this, panel);
38742         this.fireEvent("invalidated");
38743     },
38744     
38745     /**
38746      * Show the specified panel.
38747      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38748      * @return {Roo.ContentPanel} The shown panel or null
38749      */
38750     showPanel : function(panel){
38751         panel = this.getPanel(panel);
38752         if(panel){
38753             this.setActivePanel(panel);
38754         }
38755         return panel;
38756     },
38757     
38758     /**
38759      * Get the active panel for this region.
38760      * @return {Roo.ContentPanel} The active panel or null
38761      */
38762     getActivePanel : function(){
38763         return this.activePanel;
38764     },
38765     
38766     /**
38767      * Add the passed ContentPanel(s)
38768      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38769      * @return {Roo.ContentPanel} The panel added (if only one was added)
38770      */
38771     add : function(panel){
38772         if(arguments.length > 1){
38773             for(var i = 0, len = arguments.length; i < len; i++) {
38774                 this.add(arguments[i]);
38775             }
38776             return null;
38777         }
38778         if(this.hasPanel(panel)){
38779             this.showPanel(panel);
38780             return panel;
38781         }
38782         var el = panel.getEl();
38783         if(el.dom.parentNode != this.mgr.el.dom){
38784             this.mgr.el.dom.appendChild(el.dom);
38785         }
38786         if(panel.setRegion){
38787             panel.setRegion(this);
38788         }
38789         this.panels.add(panel);
38790         el.setStyle("position", "absolute");
38791         if(!panel.background){
38792             this.setActivePanel(panel);
38793             if(this.config.initialSize && this.panels.getCount()==1){
38794                 this.resizeTo(this.config.initialSize);
38795             }
38796         }
38797         this.fireEvent("paneladded", this, panel);
38798         return panel;
38799     },
38800     
38801     /**
38802      * Returns true if the panel is in this region.
38803      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38804      * @return {Boolean}
38805      */
38806     hasPanel : function(panel){
38807         if(typeof panel == "object"){ // must be panel obj
38808             panel = panel.getId();
38809         }
38810         return this.getPanel(panel) ? true : false;
38811     },
38812     
38813     /**
38814      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38815      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38816      * @param {Boolean} preservePanel Overrides the config preservePanel option
38817      * @return {Roo.ContentPanel} The panel that was removed
38818      */
38819     remove : function(panel, preservePanel){
38820         panel = this.getPanel(panel);
38821         if(!panel){
38822             return null;
38823         }
38824         var e = {};
38825         this.fireEvent("beforeremove", this, panel, e);
38826         if(e.cancel === true){
38827             return null;
38828         }
38829         var panelId = panel.getId();
38830         this.panels.removeKey(panelId);
38831         return panel;
38832     },
38833     
38834     /**
38835      * Returns the panel specified or null if it's not in this region.
38836      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38837      * @return {Roo.ContentPanel}
38838      */
38839     getPanel : function(id){
38840         if(typeof id == "object"){ // must be panel obj
38841             return id;
38842         }
38843         return this.panels.get(id);
38844     },
38845     
38846     /**
38847      * Returns this regions position (north/south/east/west/center).
38848      * @return {String} 
38849      */
38850     getPosition: function(){
38851         return this.position;    
38852     }
38853 });/*
38854  * Based on:
38855  * Ext JS Library 1.1.1
38856  * Copyright(c) 2006-2007, Ext JS, LLC.
38857  *
38858  * Originally Released Under LGPL - original licence link has changed is not relivant.
38859  *
38860  * Fork - LGPL
38861  * <script type="text/javascript">
38862  */
38863  
38864 /**
38865  * @class Roo.bootstrap.layout.Region
38866  * @extends Roo.bootstrap.layout.Basic
38867  * This class represents a region in a layout manager.
38868  
38869  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38870  * @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})
38871  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38872  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38873  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38874  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38875  * @cfg {String}    title           The title for the region (overrides panel titles)
38876  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38877  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38878  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38879  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38880  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38881  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38882  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38883  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38884  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38885  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38886
38887  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38888  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38889  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38890  * @cfg {Number}    width           For East/West panels
38891  * @cfg {Number}    height          For North/South panels
38892  * @cfg {Boolean}   split           To show the splitter
38893  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38894  * 
38895  * @cfg {string}   cls             Extra CSS classes to add to region
38896  * 
38897  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38898  * @cfg {string}   region  the region that it inhabits..
38899  *
38900
38901  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38902  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38903
38904  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38905  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38906  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38907  */
38908 Roo.bootstrap.layout.Region = function(config)
38909 {
38910     this.applyConfig(config);
38911
38912     var mgr = config.mgr;
38913     var pos = config.region;
38914     config.skipConfig = true;
38915     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38916     
38917     if (mgr.el) {
38918         this.onRender(mgr.el);   
38919     }
38920      
38921     this.visible = true;
38922     this.collapsed = false;
38923     this.unrendered_panels = [];
38924 };
38925
38926 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38927
38928     position: '', // set by wrapper (eg. north/south etc..)
38929     unrendered_panels : null,  // unrendered panels.
38930     
38931     tabPosition : false,
38932     
38933     mgr: false, // points to 'Border'
38934     
38935     
38936     createBody : function(){
38937         /** This region's body element 
38938         * @type Roo.Element */
38939         this.bodyEl = this.el.createChild({
38940                 tag: "div",
38941                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38942         });
38943     },
38944
38945     onRender: function(ctr, pos)
38946     {
38947         var dh = Roo.DomHelper;
38948         /** This region's container element 
38949         * @type Roo.Element */
38950         this.el = dh.append(ctr.dom, {
38951                 tag: "div",
38952                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38953             }, true);
38954         /** This region's title element 
38955         * @type Roo.Element */
38956     
38957         this.titleEl = dh.append(this.el.dom,  {
38958                 tag: "div",
38959                 unselectable: "on",
38960                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38961                 children:[
38962                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38963                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38964                 ]
38965             }, true);
38966         
38967         this.titleEl.enableDisplayMode();
38968         /** This region's title text element 
38969         * @type HTMLElement */
38970         this.titleTextEl = this.titleEl.dom.firstChild;
38971         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38972         /*
38973         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38974         this.closeBtn.enableDisplayMode();
38975         this.closeBtn.on("click", this.closeClicked, this);
38976         this.closeBtn.hide();
38977     */
38978         this.createBody(this.config);
38979         if(this.config.hideWhenEmpty){
38980             this.hide();
38981             this.on("paneladded", this.validateVisibility, this);
38982             this.on("panelremoved", this.validateVisibility, this);
38983         }
38984         if(this.autoScroll){
38985             this.bodyEl.setStyle("overflow", "auto");
38986         }else{
38987             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38988         }
38989         //if(c.titlebar !== false){
38990             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38991                 this.titleEl.hide();
38992             }else{
38993                 this.titleEl.show();
38994                 if(this.config.title){
38995                     this.titleTextEl.innerHTML = this.config.title;
38996                 }
38997             }
38998         //}
38999         if(this.config.collapsed){
39000             this.collapse(true);
39001         }
39002         if(this.config.hidden){
39003             this.hide();
39004         }
39005         
39006         if (this.unrendered_panels && this.unrendered_panels.length) {
39007             for (var i =0;i< this.unrendered_panels.length; i++) {
39008                 this.add(this.unrendered_panels[i]);
39009             }
39010             this.unrendered_panels = null;
39011             
39012         }
39013         
39014     },
39015     
39016     applyConfig : function(c)
39017     {
39018         /*
39019          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39020             var dh = Roo.DomHelper;
39021             if(c.titlebar !== false){
39022                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39023                 this.collapseBtn.on("click", this.collapse, this);
39024                 this.collapseBtn.enableDisplayMode();
39025                 /*
39026                 if(c.showPin === true || this.showPin){
39027                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39028                     this.stickBtn.enableDisplayMode();
39029                     this.stickBtn.on("click", this.expand, this);
39030                     this.stickBtn.hide();
39031                 }
39032                 
39033             }
39034             */
39035             /** This region's collapsed element
39036             * @type Roo.Element */
39037             /*
39038              *
39039             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39040                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39041             ]}, true);
39042             
39043             if(c.floatable !== false){
39044                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39045                this.collapsedEl.on("click", this.collapseClick, this);
39046             }
39047
39048             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39049                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39050                    id: "message", unselectable: "on", style:{"float":"left"}});
39051                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39052              }
39053             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39054             this.expandBtn.on("click", this.expand, this);
39055             
39056         }
39057         
39058         if(this.collapseBtn){
39059             this.collapseBtn.setVisible(c.collapsible == true);
39060         }
39061         
39062         this.cmargins = c.cmargins || this.cmargins ||
39063                          (this.position == "west" || this.position == "east" ?
39064                              {top: 0, left: 2, right:2, bottom: 0} :
39065                              {top: 2, left: 0, right:0, bottom: 2});
39066         */
39067         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39068         
39069         
39070         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39071         
39072         this.autoScroll = c.autoScroll || false;
39073         
39074         
39075        
39076         
39077         this.duration = c.duration || .30;
39078         this.slideDuration = c.slideDuration || .45;
39079         this.config = c;
39080        
39081     },
39082     /**
39083      * Returns true if this region is currently visible.
39084      * @return {Boolean}
39085      */
39086     isVisible : function(){
39087         return this.visible;
39088     },
39089
39090     /**
39091      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39092      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39093      */
39094     //setCollapsedTitle : function(title){
39095     //    title = title || "&#160;";
39096      //   if(this.collapsedTitleTextEl){
39097       //      this.collapsedTitleTextEl.innerHTML = title;
39098        // }
39099     //},
39100
39101     getBox : function(){
39102         var b;
39103       //  if(!this.collapsed){
39104             b = this.el.getBox(false, true);
39105        // }else{
39106           //  b = this.collapsedEl.getBox(false, true);
39107         //}
39108         return b;
39109     },
39110
39111     getMargins : function(){
39112         return this.margins;
39113         //return this.collapsed ? this.cmargins : this.margins;
39114     },
39115 /*
39116     highlight : function(){
39117         this.el.addClass("x-layout-panel-dragover");
39118     },
39119
39120     unhighlight : function(){
39121         this.el.removeClass("x-layout-panel-dragover");
39122     },
39123 */
39124     updateBox : function(box)
39125     {
39126         if (!this.bodyEl) {
39127             return; // not rendered yet..
39128         }
39129         
39130         this.box = box;
39131         if(!this.collapsed){
39132             this.el.dom.style.left = box.x + "px";
39133             this.el.dom.style.top = box.y + "px";
39134             this.updateBody(box.width, box.height);
39135         }else{
39136             this.collapsedEl.dom.style.left = box.x + "px";
39137             this.collapsedEl.dom.style.top = box.y + "px";
39138             this.collapsedEl.setSize(box.width, box.height);
39139         }
39140         if(this.tabs){
39141             this.tabs.autoSizeTabs();
39142         }
39143     },
39144
39145     updateBody : function(w, h)
39146     {
39147         if(w !== null){
39148             this.el.setWidth(w);
39149             w -= this.el.getBorderWidth("rl");
39150             if(this.config.adjustments){
39151                 w += this.config.adjustments[0];
39152             }
39153         }
39154         if(h !== null && h > 0){
39155             this.el.setHeight(h);
39156             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39157             h -= this.el.getBorderWidth("tb");
39158             if(this.config.adjustments){
39159                 h += this.config.adjustments[1];
39160             }
39161             this.bodyEl.setHeight(h);
39162             if(this.tabs){
39163                 h = this.tabs.syncHeight(h);
39164             }
39165         }
39166         if(this.panelSize){
39167             w = w !== null ? w : this.panelSize.width;
39168             h = h !== null ? h : this.panelSize.height;
39169         }
39170         if(this.activePanel){
39171             var el = this.activePanel.getEl();
39172             w = w !== null ? w : el.getWidth();
39173             h = h !== null ? h : el.getHeight();
39174             this.panelSize = {width: w, height: h};
39175             this.activePanel.setSize(w, h);
39176         }
39177         if(Roo.isIE && this.tabs){
39178             this.tabs.el.repaint();
39179         }
39180     },
39181
39182     /**
39183      * Returns the container element for this region.
39184      * @return {Roo.Element}
39185      */
39186     getEl : function(){
39187         return this.el;
39188     },
39189
39190     /**
39191      * Hides this region.
39192      */
39193     hide : function(){
39194         //if(!this.collapsed){
39195             this.el.dom.style.left = "-2000px";
39196             this.el.hide();
39197         //}else{
39198          //   this.collapsedEl.dom.style.left = "-2000px";
39199          //   this.collapsedEl.hide();
39200        // }
39201         this.visible = false;
39202         this.fireEvent("visibilitychange", this, false);
39203     },
39204
39205     /**
39206      * Shows this region if it was previously hidden.
39207      */
39208     show : function(){
39209         //if(!this.collapsed){
39210             this.el.show();
39211         //}else{
39212         //    this.collapsedEl.show();
39213        // }
39214         this.visible = true;
39215         this.fireEvent("visibilitychange", this, true);
39216     },
39217 /*
39218     closeClicked : function(){
39219         if(this.activePanel){
39220             this.remove(this.activePanel);
39221         }
39222     },
39223
39224     collapseClick : function(e){
39225         if(this.isSlid){
39226            e.stopPropagation();
39227            this.slideIn();
39228         }else{
39229            e.stopPropagation();
39230            this.slideOut();
39231         }
39232     },
39233 */
39234     /**
39235      * Collapses this region.
39236      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39237      */
39238     /*
39239     collapse : function(skipAnim, skipCheck = false){
39240         if(this.collapsed) {
39241             return;
39242         }
39243         
39244         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39245             
39246             this.collapsed = true;
39247             if(this.split){
39248                 this.split.el.hide();
39249             }
39250             if(this.config.animate && skipAnim !== true){
39251                 this.fireEvent("invalidated", this);
39252                 this.animateCollapse();
39253             }else{
39254                 this.el.setLocation(-20000,-20000);
39255                 this.el.hide();
39256                 this.collapsedEl.show();
39257                 this.fireEvent("collapsed", this);
39258                 this.fireEvent("invalidated", this);
39259             }
39260         }
39261         
39262     },
39263 */
39264     animateCollapse : function(){
39265         // overridden
39266     },
39267
39268     /**
39269      * Expands this region if it was previously collapsed.
39270      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39271      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39272      */
39273     /*
39274     expand : function(e, skipAnim){
39275         if(e) {
39276             e.stopPropagation();
39277         }
39278         if(!this.collapsed || this.el.hasActiveFx()) {
39279             return;
39280         }
39281         if(this.isSlid){
39282             this.afterSlideIn();
39283             skipAnim = true;
39284         }
39285         this.collapsed = false;
39286         if(this.config.animate && skipAnim !== true){
39287             this.animateExpand();
39288         }else{
39289             this.el.show();
39290             if(this.split){
39291                 this.split.el.show();
39292             }
39293             this.collapsedEl.setLocation(-2000,-2000);
39294             this.collapsedEl.hide();
39295             this.fireEvent("invalidated", this);
39296             this.fireEvent("expanded", this);
39297         }
39298     },
39299 */
39300     animateExpand : function(){
39301         // overridden
39302     },
39303
39304     initTabs : function()
39305     {
39306         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39307         
39308         var ts = new Roo.bootstrap.panel.Tabs({
39309             el: this.bodyEl.dom,
39310             region : this,
39311             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39312             disableTooltips: this.config.disableTabTips,
39313             toolbar : this.config.toolbar
39314         });
39315         
39316         if(this.config.hideTabs){
39317             ts.stripWrap.setDisplayed(false);
39318         }
39319         this.tabs = ts;
39320         ts.resizeTabs = this.config.resizeTabs === true;
39321         ts.minTabWidth = this.config.minTabWidth || 40;
39322         ts.maxTabWidth = this.config.maxTabWidth || 250;
39323         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39324         ts.monitorResize = false;
39325         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39326         ts.bodyEl.addClass('roo-layout-tabs-body');
39327         this.panels.each(this.initPanelAsTab, this);
39328     },
39329
39330     initPanelAsTab : function(panel){
39331         var ti = this.tabs.addTab(
39332             panel.getEl().id,
39333             panel.getTitle(),
39334             null,
39335             this.config.closeOnTab && panel.isClosable(),
39336             panel.tpl
39337         );
39338         if(panel.tabTip !== undefined){
39339             ti.setTooltip(panel.tabTip);
39340         }
39341         ti.on("activate", function(){
39342               this.setActivePanel(panel);
39343         }, this);
39344         
39345         if(this.config.closeOnTab){
39346             ti.on("beforeclose", function(t, e){
39347                 e.cancel = true;
39348                 this.remove(panel);
39349             }, this);
39350         }
39351         
39352         panel.tabItem = ti;
39353         
39354         return ti;
39355     },
39356
39357     updatePanelTitle : function(panel, title)
39358     {
39359         if(this.activePanel == panel){
39360             this.updateTitle(title);
39361         }
39362         if(this.tabs){
39363             var ti = this.tabs.getTab(panel.getEl().id);
39364             ti.setText(title);
39365             if(panel.tabTip !== undefined){
39366                 ti.setTooltip(panel.tabTip);
39367             }
39368         }
39369     },
39370
39371     updateTitle : function(title){
39372         if(this.titleTextEl && !this.config.title){
39373             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39374         }
39375     },
39376
39377     setActivePanel : function(panel)
39378     {
39379         panel = this.getPanel(panel);
39380         if(this.activePanel && this.activePanel != panel){
39381             if(this.activePanel.setActiveState(false) === false){
39382                 return;
39383             }
39384         }
39385         this.activePanel = panel;
39386         panel.setActiveState(true);
39387         if(this.panelSize){
39388             panel.setSize(this.panelSize.width, this.panelSize.height);
39389         }
39390         if(this.closeBtn){
39391             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39392         }
39393         this.updateTitle(panel.getTitle());
39394         if(this.tabs){
39395             this.fireEvent("invalidated", this);
39396         }
39397         this.fireEvent("panelactivated", this, panel);
39398     },
39399
39400     /**
39401      * Shows the specified panel.
39402      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39403      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39404      */
39405     showPanel : function(panel)
39406     {
39407         panel = this.getPanel(panel);
39408         if(panel){
39409             if(this.tabs){
39410                 var tab = this.tabs.getTab(panel.getEl().id);
39411                 if(tab.isHidden()){
39412                     this.tabs.unhideTab(tab.id);
39413                 }
39414                 tab.activate();
39415             }else{
39416                 this.setActivePanel(panel);
39417             }
39418         }
39419         return panel;
39420     },
39421
39422     /**
39423      * Get the active panel for this region.
39424      * @return {Roo.ContentPanel} The active panel or null
39425      */
39426     getActivePanel : function(){
39427         return this.activePanel;
39428     },
39429
39430     validateVisibility : function(){
39431         if(this.panels.getCount() < 1){
39432             this.updateTitle("&#160;");
39433             this.closeBtn.hide();
39434             this.hide();
39435         }else{
39436             if(!this.isVisible()){
39437                 this.show();
39438             }
39439         }
39440     },
39441
39442     /**
39443      * Adds the passed ContentPanel(s) to this region.
39444      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39445      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39446      */
39447     add : function(panel)
39448     {
39449         if(arguments.length > 1){
39450             for(var i = 0, len = arguments.length; i < len; i++) {
39451                 this.add(arguments[i]);
39452             }
39453             return null;
39454         }
39455         
39456         // if we have not been rendered yet, then we can not really do much of this..
39457         if (!this.bodyEl) {
39458             this.unrendered_panels.push(panel);
39459             return panel;
39460         }
39461         
39462         
39463         
39464         
39465         if(this.hasPanel(panel)){
39466             this.showPanel(panel);
39467             return panel;
39468         }
39469         panel.setRegion(this);
39470         this.panels.add(panel);
39471        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39472             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39473             // and hide them... ???
39474             this.bodyEl.dom.appendChild(panel.getEl().dom);
39475             if(panel.background !== true){
39476                 this.setActivePanel(panel);
39477             }
39478             this.fireEvent("paneladded", this, panel);
39479             return panel;
39480         }
39481         */
39482         if(!this.tabs){
39483             this.initTabs();
39484         }else{
39485             this.initPanelAsTab(panel);
39486         }
39487         
39488         
39489         if(panel.background !== true){
39490             this.tabs.activate(panel.getEl().id);
39491         }
39492         this.fireEvent("paneladded", this, panel);
39493         return panel;
39494     },
39495
39496     /**
39497      * Hides the tab for the specified panel.
39498      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39499      */
39500     hidePanel : function(panel){
39501         if(this.tabs && (panel = this.getPanel(panel))){
39502             this.tabs.hideTab(panel.getEl().id);
39503         }
39504     },
39505
39506     /**
39507      * Unhides the tab for a previously hidden panel.
39508      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39509      */
39510     unhidePanel : function(panel){
39511         if(this.tabs && (panel = this.getPanel(panel))){
39512             this.tabs.unhideTab(panel.getEl().id);
39513         }
39514     },
39515
39516     clearPanels : function(){
39517         while(this.panels.getCount() > 0){
39518              this.remove(this.panels.first());
39519         }
39520     },
39521
39522     /**
39523      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39524      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39525      * @param {Boolean} preservePanel Overrides the config preservePanel option
39526      * @return {Roo.ContentPanel} The panel that was removed
39527      */
39528     remove : function(panel, preservePanel)
39529     {
39530         panel = this.getPanel(panel);
39531         if(!panel){
39532             return null;
39533         }
39534         var e = {};
39535         this.fireEvent("beforeremove", this, panel, e);
39536         if(e.cancel === true){
39537             return null;
39538         }
39539         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39540         var panelId = panel.getId();
39541         this.panels.removeKey(panelId);
39542         if(preservePanel){
39543             document.body.appendChild(panel.getEl().dom);
39544         }
39545         if(this.tabs){
39546             this.tabs.removeTab(panel.getEl().id);
39547         }else if (!preservePanel){
39548             this.bodyEl.dom.removeChild(panel.getEl().dom);
39549         }
39550         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39551             var p = this.panels.first();
39552             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39553             tempEl.appendChild(p.getEl().dom);
39554             this.bodyEl.update("");
39555             this.bodyEl.dom.appendChild(p.getEl().dom);
39556             tempEl = null;
39557             this.updateTitle(p.getTitle());
39558             this.tabs = null;
39559             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39560             this.setActivePanel(p);
39561         }
39562         panel.setRegion(null);
39563         if(this.activePanel == panel){
39564             this.activePanel = null;
39565         }
39566         if(this.config.autoDestroy !== false && preservePanel !== true){
39567             try{panel.destroy();}catch(e){}
39568         }
39569         this.fireEvent("panelremoved", this, panel);
39570         return panel;
39571     },
39572
39573     /**
39574      * Returns the TabPanel component used by this region
39575      * @return {Roo.TabPanel}
39576      */
39577     getTabs : function(){
39578         return this.tabs;
39579     },
39580
39581     createTool : function(parentEl, className){
39582         var btn = Roo.DomHelper.append(parentEl, {
39583             tag: "div",
39584             cls: "x-layout-tools-button",
39585             children: [ {
39586                 tag: "div",
39587                 cls: "roo-layout-tools-button-inner " + className,
39588                 html: "&#160;"
39589             }]
39590         }, true);
39591         btn.addClassOnOver("roo-layout-tools-button-over");
39592         return btn;
39593     }
39594 });/*
39595  * Based on:
39596  * Ext JS Library 1.1.1
39597  * Copyright(c) 2006-2007, Ext JS, LLC.
39598  *
39599  * Originally Released Under LGPL - original licence link has changed is not relivant.
39600  *
39601  * Fork - LGPL
39602  * <script type="text/javascript">
39603  */
39604  
39605
39606
39607 /**
39608  * @class Roo.SplitLayoutRegion
39609  * @extends Roo.LayoutRegion
39610  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39611  */
39612 Roo.bootstrap.layout.Split = function(config){
39613     this.cursor = config.cursor;
39614     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39615 };
39616
39617 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39618 {
39619     splitTip : "Drag to resize.",
39620     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39621     useSplitTips : false,
39622
39623     applyConfig : function(config){
39624         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39625     },
39626     
39627     onRender : function(ctr,pos) {
39628         
39629         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39630         if(!this.config.split){
39631             return;
39632         }
39633         if(!this.split){
39634             
39635             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39636                             tag: "div",
39637                             id: this.el.id + "-split",
39638                             cls: "roo-layout-split roo-layout-split-"+this.position,
39639                             html: "&#160;"
39640             });
39641             /** The SplitBar for this region 
39642             * @type Roo.SplitBar */
39643             // does not exist yet...
39644             Roo.log([this.position, this.orientation]);
39645             
39646             this.split = new Roo.bootstrap.SplitBar({
39647                 dragElement : splitEl,
39648                 resizingElement: this.el,
39649                 orientation : this.orientation
39650             });
39651             
39652             this.split.on("moved", this.onSplitMove, this);
39653             this.split.useShim = this.config.useShim === true;
39654             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39655             if(this.useSplitTips){
39656                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39657             }
39658             //if(config.collapsible){
39659             //    this.split.el.on("dblclick", this.collapse,  this);
39660             //}
39661         }
39662         if(typeof this.config.minSize != "undefined"){
39663             this.split.minSize = this.config.minSize;
39664         }
39665         if(typeof this.config.maxSize != "undefined"){
39666             this.split.maxSize = this.config.maxSize;
39667         }
39668         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39669             this.hideSplitter();
39670         }
39671         
39672     },
39673
39674     getHMaxSize : function(){
39675          var cmax = this.config.maxSize || 10000;
39676          var center = this.mgr.getRegion("center");
39677          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39678     },
39679
39680     getVMaxSize : function(){
39681          var cmax = this.config.maxSize || 10000;
39682          var center = this.mgr.getRegion("center");
39683          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39684     },
39685
39686     onSplitMove : function(split, newSize){
39687         this.fireEvent("resized", this, newSize);
39688     },
39689     
39690     /** 
39691      * Returns the {@link Roo.SplitBar} for this region.
39692      * @return {Roo.SplitBar}
39693      */
39694     getSplitBar : function(){
39695         return this.split;
39696     },
39697     
39698     hide : function(){
39699         this.hideSplitter();
39700         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39701     },
39702
39703     hideSplitter : function(){
39704         if(this.split){
39705             this.split.el.setLocation(-2000,-2000);
39706             this.split.el.hide();
39707         }
39708     },
39709
39710     show : function(){
39711         if(this.split){
39712             this.split.el.show();
39713         }
39714         Roo.bootstrap.layout.Split.superclass.show.call(this);
39715     },
39716     
39717     beforeSlide: function(){
39718         if(Roo.isGecko){// firefox overflow auto bug workaround
39719             this.bodyEl.clip();
39720             if(this.tabs) {
39721                 this.tabs.bodyEl.clip();
39722             }
39723             if(this.activePanel){
39724                 this.activePanel.getEl().clip();
39725                 
39726                 if(this.activePanel.beforeSlide){
39727                     this.activePanel.beforeSlide();
39728                 }
39729             }
39730         }
39731     },
39732     
39733     afterSlide : function(){
39734         if(Roo.isGecko){// firefox overflow auto bug workaround
39735             this.bodyEl.unclip();
39736             if(this.tabs) {
39737                 this.tabs.bodyEl.unclip();
39738             }
39739             if(this.activePanel){
39740                 this.activePanel.getEl().unclip();
39741                 if(this.activePanel.afterSlide){
39742                     this.activePanel.afterSlide();
39743                 }
39744             }
39745         }
39746     },
39747
39748     initAutoHide : function(){
39749         if(this.autoHide !== false){
39750             if(!this.autoHideHd){
39751                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39752                 this.autoHideHd = {
39753                     "mouseout": function(e){
39754                         if(!e.within(this.el, true)){
39755                             st.delay(500);
39756                         }
39757                     },
39758                     "mouseover" : function(e){
39759                         st.cancel();
39760                     },
39761                     scope : this
39762                 };
39763             }
39764             this.el.on(this.autoHideHd);
39765         }
39766     },
39767
39768     clearAutoHide : function(){
39769         if(this.autoHide !== false){
39770             this.el.un("mouseout", this.autoHideHd.mouseout);
39771             this.el.un("mouseover", this.autoHideHd.mouseover);
39772         }
39773     },
39774
39775     clearMonitor : function(){
39776         Roo.get(document).un("click", this.slideInIf, this);
39777     },
39778
39779     // these names are backwards but not changed for compat
39780     slideOut : function(){
39781         if(this.isSlid || this.el.hasActiveFx()){
39782             return;
39783         }
39784         this.isSlid = true;
39785         if(this.collapseBtn){
39786             this.collapseBtn.hide();
39787         }
39788         this.closeBtnState = this.closeBtn.getStyle('display');
39789         this.closeBtn.hide();
39790         if(this.stickBtn){
39791             this.stickBtn.show();
39792         }
39793         this.el.show();
39794         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39795         this.beforeSlide();
39796         this.el.setStyle("z-index", 10001);
39797         this.el.slideIn(this.getSlideAnchor(), {
39798             callback: function(){
39799                 this.afterSlide();
39800                 this.initAutoHide();
39801                 Roo.get(document).on("click", this.slideInIf, this);
39802                 this.fireEvent("slideshow", this);
39803             },
39804             scope: this,
39805             block: true
39806         });
39807     },
39808
39809     afterSlideIn : function(){
39810         this.clearAutoHide();
39811         this.isSlid = false;
39812         this.clearMonitor();
39813         this.el.setStyle("z-index", "");
39814         if(this.collapseBtn){
39815             this.collapseBtn.show();
39816         }
39817         this.closeBtn.setStyle('display', this.closeBtnState);
39818         if(this.stickBtn){
39819             this.stickBtn.hide();
39820         }
39821         this.fireEvent("slidehide", this);
39822     },
39823
39824     slideIn : function(cb){
39825         if(!this.isSlid || this.el.hasActiveFx()){
39826             Roo.callback(cb);
39827             return;
39828         }
39829         this.isSlid = false;
39830         this.beforeSlide();
39831         this.el.slideOut(this.getSlideAnchor(), {
39832             callback: function(){
39833                 this.el.setLeftTop(-10000, -10000);
39834                 this.afterSlide();
39835                 this.afterSlideIn();
39836                 Roo.callback(cb);
39837             },
39838             scope: this,
39839             block: true
39840         });
39841     },
39842     
39843     slideInIf : function(e){
39844         if(!e.within(this.el)){
39845             this.slideIn();
39846         }
39847     },
39848
39849     animateCollapse : function(){
39850         this.beforeSlide();
39851         this.el.setStyle("z-index", 20000);
39852         var anchor = this.getSlideAnchor();
39853         this.el.slideOut(anchor, {
39854             callback : function(){
39855                 this.el.setStyle("z-index", "");
39856                 this.collapsedEl.slideIn(anchor, {duration:.3});
39857                 this.afterSlide();
39858                 this.el.setLocation(-10000,-10000);
39859                 this.el.hide();
39860                 this.fireEvent("collapsed", this);
39861             },
39862             scope: this,
39863             block: true
39864         });
39865     },
39866
39867     animateExpand : function(){
39868         this.beforeSlide();
39869         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39870         this.el.setStyle("z-index", 20000);
39871         this.collapsedEl.hide({
39872             duration:.1
39873         });
39874         this.el.slideIn(this.getSlideAnchor(), {
39875             callback : function(){
39876                 this.el.setStyle("z-index", "");
39877                 this.afterSlide();
39878                 if(this.split){
39879                     this.split.el.show();
39880                 }
39881                 this.fireEvent("invalidated", this);
39882                 this.fireEvent("expanded", this);
39883             },
39884             scope: this,
39885             block: true
39886         });
39887     },
39888
39889     anchors : {
39890         "west" : "left",
39891         "east" : "right",
39892         "north" : "top",
39893         "south" : "bottom"
39894     },
39895
39896     sanchors : {
39897         "west" : "l",
39898         "east" : "r",
39899         "north" : "t",
39900         "south" : "b"
39901     },
39902
39903     canchors : {
39904         "west" : "tl-tr",
39905         "east" : "tr-tl",
39906         "north" : "tl-bl",
39907         "south" : "bl-tl"
39908     },
39909
39910     getAnchor : function(){
39911         return this.anchors[this.position];
39912     },
39913
39914     getCollapseAnchor : function(){
39915         return this.canchors[this.position];
39916     },
39917
39918     getSlideAnchor : function(){
39919         return this.sanchors[this.position];
39920     },
39921
39922     getAlignAdj : function(){
39923         var cm = this.cmargins;
39924         switch(this.position){
39925             case "west":
39926                 return [0, 0];
39927             break;
39928             case "east":
39929                 return [0, 0];
39930             break;
39931             case "north":
39932                 return [0, 0];
39933             break;
39934             case "south":
39935                 return [0, 0];
39936             break;
39937         }
39938     },
39939
39940     getExpandAdj : function(){
39941         var c = this.collapsedEl, cm = this.cmargins;
39942         switch(this.position){
39943             case "west":
39944                 return [-(cm.right+c.getWidth()+cm.left), 0];
39945             break;
39946             case "east":
39947                 return [cm.right+c.getWidth()+cm.left, 0];
39948             break;
39949             case "north":
39950                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39951             break;
39952             case "south":
39953                 return [0, cm.top+cm.bottom+c.getHeight()];
39954             break;
39955         }
39956     }
39957 });/*
39958  * Based on:
39959  * Ext JS Library 1.1.1
39960  * Copyright(c) 2006-2007, Ext JS, LLC.
39961  *
39962  * Originally Released Under LGPL - original licence link has changed is not relivant.
39963  *
39964  * Fork - LGPL
39965  * <script type="text/javascript">
39966  */
39967 /*
39968  * These classes are private internal classes
39969  */
39970 Roo.bootstrap.layout.Center = function(config){
39971     config.region = "center";
39972     Roo.bootstrap.layout.Region.call(this, config);
39973     this.visible = true;
39974     this.minWidth = config.minWidth || 20;
39975     this.minHeight = config.minHeight || 20;
39976 };
39977
39978 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39979     hide : function(){
39980         // center panel can't be hidden
39981     },
39982     
39983     show : function(){
39984         // center panel can't be hidden
39985     },
39986     
39987     getMinWidth: function(){
39988         return this.minWidth;
39989     },
39990     
39991     getMinHeight: function(){
39992         return this.minHeight;
39993     }
39994 });
39995
39996
39997
39998
39999  
40000
40001
40002
40003
40004
40005
40006 Roo.bootstrap.layout.North = function(config)
40007 {
40008     config.region = 'north';
40009     config.cursor = 'n-resize';
40010     
40011     Roo.bootstrap.layout.Split.call(this, config);
40012     
40013     
40014     if(this.split){
40015         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40016         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40017         this.split.el.addClass("roo-layout-split-v");
40018     }
40019     //var size = config.initialSize || config.height;
40020     //if(this.el && typeof size != "undefined"){
40021     //    this.el.setHeight(size);
40022     //}
40023 };
40024 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40025 {
40026     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40027      
40028      
40029     onRender : function(ctr, pos)
40030     {
40031         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40032         var size = this.config.initialSize || this.config.height;
40033         if(this.el && typeof size != "undefined"){
40034             this.el.setHeight(size);
40035         }
40036     
40037     },
40038     
40039     getBox : function(){
40040         if(this.collapsed){
40041             return this.collapsedEl.getBox();
40042         }
40043         var box = this.el.getBox();
40044         if(this.split){
40045             box.height += this.split.el.getHeight();
40046         }
40047         return box;
40048     },
40049     
40050     updateBox : function(box){
40051         if(this.split && !this.collapsed){
40052             box.height -= this.split.el.getHeight();
40053             this.split.el.setLeft(box.x);
40054             this.split.el.setTop(box.y+box.height);
40055             this.split.el.setWidth(box.width);
40056         }
40057         if(this.collapsed){
40058             this.updateBody(box.width, null);
40059         }
40060         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40061     }
40062 });
40063
40064
40065
40066
40067
40068 Roo.bootstrap.layout.South = function(config){
40069     config.region = 'south';
40070     config.cursor = 's-resize';
40071     Roo.bootstrap.layout.Split.call(this, config);
40072     if(this.split){
40073         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40074         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40075         this.split.el.addClass("roo-layout-split-v");
40076     }
40077     
40078 };
40079
40080 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40081     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40082     
40083     onRender : function(ctr, pos)
40084     {
40085         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40086         var size = this.config.initialSize || this.config.height;
40087         if(this.el && typeof size != "undefined"){
40088             this.el.setHeight(size);
40089         }
40090     
40091     },
40092     
40093     getBox : function(){
40094         if(this.collapsed){
40095             return this.collapsedEl.getBox();
40096         }
40097         var box = this.el.getBox();
40098         if(this.split){
40099             var sh = this.split.el.getHeight();
40100             box.height += sh;
40101             box.y -= sh;
40102         }
40103         return box;
40104     },
40105     
40106     updateBox : function(box){
40107         if(this.split && !this.collapsed){
40108             var sh = this.split.el.getHeight();
40109             box.height -= sh;
40110             box.y += sh;
40111             this.split.el.setLeft(box.x);
40112             this.split.el.setTop(box.y-sh);
40113             this.split.el.setWidth(box.width);
40114         }
40115         if(this.collapsed){
40116             this.updateBody(box.width, null);
40117         }
40118         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40119     }
40120 });
40121
40122 Roo.bootstrap.layout.East = function(config){
40123     config.region = "east";
40124     config.cursor = "e-resize";
40125     Roo.bootstrap.layout.Split.call(this, config);
40126     if(this.split){
40127         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40128         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40129         this.split.el.addClass("roo-layout-split-h");
40130     }
40131     
40132 };
40133 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40134     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40135     
40136     onRender : function(ctr, pos)
40137     {
40138         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40139         var size = this.config.initialSize || this.config.width;
40140         if(this.el && typeof size != "undefined"){
40141             this.el.setWidth(size);
40142         }
40143     
40144     },
40145     
40146     getBox : function(){
40147         if(this.collapsed){
40148             return this.collapsedEl.getBox();
40149         }
40150         var box = this.el.getBox();
40151         if(this.split){
40152             var sw = this.split.el.getWidth();
40153             box.width += sw;
40154             box.x -= sw;
40155         }
40156         return box;
40157     },
40158
40159     updateBox : function(box){
40160         if(this.split && !this.collapsed){
40161             var sw = this.split.el.getWidth();
40162             box.width -= sw;
40163             this.split.el.setLeft(box.x);
40164             this.split.el.setTop(box.y);
40165             this.split.el.setHeight(box.height);
40166             box.x += sw;
40167         }
40168         if(this.collapsed){
40169             this.updateBody(null, box.height);
40170         }
40171         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40172     }
40173 });
40174
40175 Roo.bootstrap.layout.West = function(config){
40176     config.region = "west";
40177     config.cursor = "w-resize";
40178     
40179     Roo.bootstrap.layout.Split.call(this, config);
40180     if(this.split){
40181         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40182         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40183         this.split.el.addClass("roo-layout-split-h");
40184     }
40185     
40186 };
40187 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40188     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40189     
40190     onRender: function(ctr, pos)
40191     {
40192         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40193         var size = this.config.initialSize || this.config.width;
40194         if(typeof size != "undefined"){
40195             this.el.setWidth(size);
40196         }
40197     },
40198     
40199     getBox : function(){
40200         if(this.collapsed){
40201             return this.collapsedEl.getBox();
40202         }
40203         var box = this.el.getBox();
40204         if (box.width == 0) {
40205             box.width = this.config.width; // kludge?
40206         }
40207         if(this.split){
40208             box.width += this.split.el.getWidth();
40209         }
40210         return box;
40211     },
40212     
40213     updateBox : function(box){
40214         if(this.split && !this.collapsed){
40215             var sw = this.split.el.getWidth();
40216             box.width -= sw;
40217             this.split.el.setLeft(box.x+box.width);
40218             this.split.el.setTop(box.y);
40219             this.split.el.setHeight(box.height);
40220         }
40221         if(this.collapsed){
40222             this.updateBody(null, box.height);
40223         }
40224         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40225     }
40226 });/*
40227  * Based on:
40228  * Ext JS Library 1.1.1
40229  * Copyright(c) 2006-2007, Ext JS, LLC.
40230  *
40231  * Originally Released Under LGPL - original licence link has changed is not relivant.
40232  *
40233  * Fork - LGPL
40234  * <script type="text/javascript">
40235  */
40236 /**
40237  * @class Roo.bootstrap.paenl.Content
40238  * @extends Roo.util.Observable
40239  * @children Roo.bootstrap.Component
40240  * @parent builder Roo.bootstrap.layout.Border
40241  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40242  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40243  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40244  * @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
40245  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40246  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40247  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40248  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40249  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40250  * @cfg {String} title          The title for this panel
40251  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40252  * @cfg {String} url            Calls {@link #setUrl} with this value
40253  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40254  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40255  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40256  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40257  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40258  * @cfg {Boolean} badges render the badges
40259  * @cfg {String} cls  extra classes to use  
40260  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40261  
40262  * @constructor
40263  * Create a new ContentPanel.
40264  * @param {String/Object} config A string to set only the title or a config object
40265  
40266  */
40267 Roo.bootstrap.panel.Content = function( config){
40268     
40269     this.tpl = config.tpl || false;
40270     
40271     var el = config.el;
40272     var content = config.content;
40273
40274     if(config.autoCreate){ // xtype is available if this is called from factory
40275         el = Roo.id();
40276     }
40277     this.el = Roo.get(el);
40278     if(!this.el && config && config.autoCreate){
40279         if(typeof config.autoCreate == "object"){
40280             if(!config.autoCreate.id){
40281                 config.autoCreate.id = config.id||el;
40282             }
40283             this.el = Roo.DomHelper.append(document.body,
40284                         config.autoCreate, true);
40285         }else{
40286             var elcfg =  {
40287                 tag: "div",
40288                 cls: (config.cls || '') +
40289                     (config.background ? ' bg-' + config.background : '') +
40290                     " roo-layout-inactive-content",
40291                 id: config.id||el
40292             };
40293             if (config.iframe) {
40294                 elcfg.cn = [
40295                     {
40296                         tag : 'iframe',
40297                         style : 'border: 0px',
40298                         src : 'about:blank'
40299                     }
40300                 ];
40301             }
40302               
40303             if (config.html) {
40304                 elcfg.html = config.html;
40305                 
40306             }
40307                         
40308             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40309             if (config.iframe) {
40310                 this.iframeEl = this.el.select('iframe',true).first();
40311             }
40312             
40313         }
40314     } 
40315     this.closable = false;
40316     this.loaded = false;
40317     this.active = false;
40318    
40319       
40320     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40321         
40322         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40323         
40324         this.wrapEl = this.el; //this.el.wrap();
40325         var ti = [];
40326         if (config.toolbar.items) {
40327             ti = config.toolbar.items ;
40328             delete config.toolbar.items ;
40329         }
40330         
40331         var nitems = [];
40332         this.toolbar.render(this.wrapEl, 'before');
40333         for(var i =0;i < ti.length;i++) {
40334           //  Roo.log(['add child', items[i]]);
40335             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40336         }
40337         this.toolbar.items = nitems;
40338         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40339         delete config.toolbar;
40340         
40341     }
40342     /*
40343     // xtype created footer. - not sure if will work as we normally have to render first..
40344     if (this.footer && !this.footer.el && this.footer.xtype) {
40345         if (!this.wrapEl) {
40346             this.wrapEl = this.el.wrap();
40347         }
40348     
40349         this.footer.container = this.wrapEl.createChild();
40350          
40351         this.footer = Roo.factory(this.footer, Roo);
40352         
40353     }
40354     */
40355     
40356      if(typeof config == "string"){
40357         this.title = config;
40358     }else{
40359         Roo.apply(this, config);
40360     }
40361     
40362     if(this.resizeEl){
40363         this.resizeEl = Roo.get(this.resizeEl, true);
40364     }else{
40365         this.resizeEl = this.el;
40366     }
40367     // handle view.xtype
40368     
40369  
40370     
40371     
40372     this.addEvents({
40373         /**
40374          * @event activate
40375          * Fires when this panel is activated. 
40376          * @param {Roo.ContentPanel} this
40377          */
40378         "activate" : true,
40379         /**
40380          * @event deactivate
40381          * Fires when this panel is activated. 
40382          * @param {Roo.ContentPanel} this
40383          */
40384         "deactivate" : true,
40385
40386         /**
40387          * @event resize
40388          * Fires when this panel is resized if fitToFrame is true.
40389          * @param {Roo.ContentPanel} this
40390          * @param {Number} width The width after any component adjustments
40391          * @param {Number} height The height after any component adjustments
40392          */
40393         "resize" : true,
40394         
40395          /**
40396          * @event render
40397          * Fires when this tab is created
40398          * @param {Roo.ContentPanel} this
40399          */
40400         "render" : true,
40401         
40402           /**
40403          * @event scroll
40404          * Fires when this content is scrolled
40405          * @param {Roo.ContentPanel} this
40406          * @param {Event} scrollEvent
40407          */
40408         "scroll" : true
40409         
40410         
40411         
40412     });
40413     
40414
40415     
40416     
40417     if(this.autoScroll && !this.iframe){
40418         this.resizeEl.setStyle("overflow", "auto");
40419         this.resizeEl.on('scroll', this.onScroll, this);
40420     } else {
40421         // fix randome scrolling
40422         //this.el.on('scroll', function() {
40423         //    Roo.log('fix random scolling');
40424         //    this.scrollTo('top',0); 
40425         //});
40426     }
40427     content = content || this.content;
40428     if(content){
40429         this.setContent(content);
40430     }
40431     if(config && config.url){
40432         this.setUrl(this.url, this.params, this.loadOnce);
40433     }
40434     
40435     
40436     
40437     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40438     
40439     if (this.view && typeof(this.view.xtype) != 'undefined') {
40440         this.view.el = this.el.appendChild(document.createElement("div"));
40441         this.view = Roo.factory(this.view); 
40442         this.view.render  &&  this.view.render(false, '');  
40443     }
40444     
40445     
40446     this.fireEvent('render', this);
40447 };
40448
40449 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40450     
40451     cls : '',
40452     background : '',
40453     
40454     tabTip : '',
40455     
40456     iframe : false,
40457     iframeEl : false,
40458     
40459     /* Resize Element - use this to work out scroll etc. */
40460     resizeEl : false,
40461     
40462     setRegion : function(region){
40463         this.region = region;
40464         this.setActiveClass(region && !this.background);
40465     },
40466     
40467     
40468     setActiveClass: function(state)
40469     {
40470         if(state){
40471            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40472            this.el.setStyle('position','relative');
40473         }else{
40474            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40475            this.el.setStyle('position', 'absolute');
40476         } 
40477     },
40478     
40479     /**
40480      * Returns the toolbar for this Panel if one was configured. 
40481      * @return {Roo.Toolbar} 
40482      */
40483     getToolbar : function(){
40484         return this.toolbar;
40485     },
40486     
40487     setActiveState : function(active)
40488     {
40489         this.active = active;
40490         this.setActiveClass(active);
40491         if(!active){
40492             if(this.fireEvent("deactivate", this) === false){
40493                 return false;
40494             }
40495             return true;
40496         }
40497         this.fireEvent("activate", this);
40498         return true;
40499     },
40500     /**
40501      * Updates this panel's element (not for iframe)
40502      * @param {String} content The new content
40503      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40504     */
40505     setContent : function(content, loadScripts){
40506         if (this.iframe) {
40507             return;
40508         }
40509         
40510         this.el.update(content, loadScripts);
40511     },
40512
40513     ignoreResize : function(w, h)
40514     {
40515         return false; // always resize?
40516         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40517             return true;
40518         }else{
40519             this.lastSize = {width: w, height: h};
40520             return false;
40521         }
40522     },
40523     /**
40524      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40525      * @return {Roo.UpdateManager} The UpdateManager
40526      */
40527     getUpdateManager : function(){
40528         if (this.iframe) {
40529             return false;
40530         }
40531         return this.el.getUpdateManager();
40532     },
40533      /**
40534      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40535      * Does not work with IFRAME contents
40536      * @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:
40537 <pre><code>
40538 panel.load({
40539     url: "your-url.php",
40540     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40541     callback: yourFunction,
40542     scope: yourObject, //(optional scope)
40543     discardUrl: false,
40544     nocache: false,
40545     text: "Loading...",
40546     timeout: 30,
40547     scripts: false
40548 });
40549 </code></pre>
40550      
40551      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40552      * 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.
40553      * @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}
40554      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40555      * @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.
40556      * @return {Roo.ContentPanel} this
40557      */
40558     load : function(){
40559         
40560         if (this.iframe) {
40561             return this;
40562         }
40563         
40564         var um = this.el.getUpdateManager();
40565         um.update.apply(um, arguments);
40566         return this;
40567     },
40568
40569
40570     /**
40571      * 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.
40572      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40573      * @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)
40574      * @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)
40575      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40576      */
40577     setUrl : function(url, params, loadOnce){
40578         if (this.iframe) {
40579             this.iframeEl.dom.src = url;
40580             return false;
40581         }
40582         
40583         if(this.refreshDelegate){
40584             this.removeListener("activate", this.refreshDelegate);
40585         }
40586         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40587         this.on("activate", this.refreshDelegate);
40588         return this.el.getUpdateManager();
40589     },
40590     
40591     _handleRefresh : function(url, params, loadOnce){
40592         if(!loadOnce || !this.loaded){
40593             var updater = this.el.getUpdateManager();
40594             updater.update(url, params, this._setLoaded.createDelegate(this));
40595         }
40596     },
40597     
40598     _setLoaded : function(){
40599         this.loaded = true;
40600     }, 
40601     
40602     /**
40603      * Returns this panel's id
40604      * @return {String} 
40605      */
40606     getId : function(){
40607         return this.el.id;
40608     },
40609     
40610     /** 
40611      * Returns this panel's element - used by regiosn to add.
40612      * @return {Roo.Element} 
40613      */
40614     getEl : function(){
40615         return this.wrapEl || this.el;
40616     },
40617     
40618    
40619     
40620     adjustForComponents : function(width, height)
40621     {
40622         //Roo.log('adjustForComponents ');
40623         if(this.resizeEl != this.el){
40624             width -= this.el.getFrameWidth('lr');
40625             height -= this.el.getFrameWidth('tb');
40626         }
40627         if(this.toolbar){
40628             var te = this.toolbar.getEl();
40629             te.setWidth(width);
40630             height -= te.getHeight();
40631         }
40632         if(this.footer){
40633             var te = this.footer.getEl();
40634             te.setWidth(width);
40635             height -= te.getHeight();
40636         }
40637         
40638         
40639         if(this.adjustments){
40640             width += this.adjustments[0];
40641             height += this.adjustments[1];
40642         }
40643         return {"width": width, "height": height};
40644     },
40645     
40646     setSize : function(width, height){
40647         if(this.fitToFrame && !this.ignoreResize(width, height)){
40648             if(this.fitContainer && this.resizeEl != this.el){
40649                 this.el.setSize(width, height);
40650             }
40651             var size = this.adjustForComponents(width, height);
40652             if (this.iframe) {
40653                 this.iframeEl.setSize(width,height);
40654             }
40655             
40656             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40657             this.fireEvent('resize', this, size.width, size.height);
40658             
40659             
40660         }
40661     },
40662     
40663     /**
40664      * Returns this panel's title
40665      * @return {String} 
40666      */
40667     getTitle : function(){
40668         
40669         if (typeof(this.title) != 'object') {
40670             return this.title;
40671         }
40672         
40673         var t = '';
40674         for (var k in this.title) {
40675             if (!this.title.hasOwnProperty(k)) {
40676                 continue;
40677             }
40678             
40679             if (k.indexOf('-') >= 0) {
40680                 var s = k.split('-');
40681                 for (var i = 0; i<s.length; i++) {
40682                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40683                 }
40684             } else {
40685                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40686             }
40687         }
40688         return t;
40689     },
40690     
40691     /**
40692      * Set this panel's title
40693      * @param {String} title
40694      */
40695     setTitle : function(title){
40696         this.title = title;
40697         if(this.region){
40698             this.region.updatePanelTitle(this, title);
40699         }
40700     },
40701     
40702     /**
40703      * Returns true is this panel was configured to be closable
40704      * @return {Boolean} 
40705      */
40706     isClosable : function(){
40707         return this.closable;
40708     },
40709     
40710     beforeSlide : function(){
40711         this.el.clip();
40712         this.resizeEl.clip();
40713     },
40714     
40715     afterSlide : function(){
40716         this.el.unclip();
40717         this.resizeEl.unclip();
40718     },
40719     
40720     /**
40721      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40722      *   Will fail silently if the {@link #setUrl} method has not been called.
40723      *   This does not activate the panel, just updates its content.
40724      */
40725     refresh : function(){
40726         if(this.refreshDelegate){
40727            this.loaded = false;
40728            this.refreshDelegate();
40729         }
40730     },
40731     
40732     /**
40733      * Destroys this panel
40734      */
40735     destroy : function(){
40736         this.el.removeAllListeners();
40737         var tempEl = document.createElement("span");
40738         tempEl.appendChild(this.el.dom);
40739         tempEl.innerHTML = "";
40740         this.el.remove();
40741         this.el = null;
40742     },
40743     
40744     /**
40745      * form - if the content panel contains a form - this is a reference to it.
40746      * @type {Roo.form.Form}
40747      */
40748     form : false,
40749     /**
40750      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40751      *    This contains a reference to it.
40752      * @type {Roo.View}
40753      */
40754     view : false,
40755     
40756       /**
40757      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40758      * <pre><code>
40759
40760 layout.addxtype({
40761        xtype : 'Form',
40762        items: [ .... ]
40763    }
40764 );
40765
40766 </code></pre>
40767      * @param {Object} cfg Xtype definition of item to add.
40768      */
40769     
40770     
40771     getChildContainer: function () {
40772         return this.getEl();
40773     },
40774     
40775     
40776     onScroll : function(e)
40777     {
40778         this.fireEvent('scroll', this, e);
40779     }
40780     
40781     
40782     /*
40783         var  ret = new Roo.factory(cfg);
40784         return ret;
40785         
40786         
40787         // add form..
40788         if (cfg.xtype.match(/^Form$/)) {
40789             
40790             var el;
40791             //if (this.footer) {
40792             //    el = this.footer.container.insertSibling(false, 'before');
40793             //} else {
40794                 el = this.el.createChild();
40795             //}
40796
40797             this.form = new  Roo.form.Form(cfg);
40798             
40799             
40800             if ( this.form.allItems.length) {
40801                 this.form.render(el.dom);
40802             }
40803             return this.form;
40804         }
40805         // should only have one of theses..
40806         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40807             // views.. should not be just added - used named prop 'view''
40808             
40809             cfg.el = this.el.appendChild(document.createElement("div"));
40810             // factory?
40811             
40812             var ret = new Roo.factory(cfg);
40813              
40814              ret.render && ret.render(false, ''); // render blank..
40815             this.view = ret;
40816             return ret;
40817         }
40818         return false;
40819     }
40820     \*/
40821 });
40822  
40823 /**
40824  * @class Roo.bootstrap.panel.Grid
40825  * @extends Roo.bootstrap.panel.Content
40826  * @constructor
40827  * Create a new GridPanel.
40828  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40829  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40830  * @param {Object} config A the config object
40831   
40832  */
40833
40834
40835
40836 Roo.bootstrap.panel.Grid = function(config)
40837 {
40838     
40839       
40840     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40841         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40842
40843     config.el = this.wrapper;
40844     //this.el = this.wrapper;
40845     
40846       if (config.container) {
40847         // ctor'ed from a Border/panel.grid
40848         
40849         
40850         this.wrapper.setStyle("overflow", "hidden");
40851         this.wrapper.addClass('roo-grid-container');
40852
40853     }
40854     
40855     
40856     if(config.toolbar){
40857         var tool_el = this.wrapper.createChild();    
40858         this.toolbar = Roo.factory(config.toolbar);
40859         var ti = [];
40860         if (config.toolbar.items) {
40861             ti = config.toolbar.items ;
40862             delete config.toolbar.items ;
40863         }
40864         
40865         var nitems = [];
40866         this.toolbar.render(tool_el);
40867         for(var i =0;i < ti.length;i++) {
40868           //  Roo.log(['add child', items[i]]);
40869             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40870         }
40871         this.toolbar.items = nitems;
40872         
40873         delete config.toolbar;
40874     }
40875     
40876     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40877     config.grid.scrollBody = true;;
40878     config.grid.monitorWindowResize = false; // turn off autosizing
40879     config.grid.autoHeight = false;
40880     config.grid.autoWidth = false;
40881     
40882     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40883     
40884     if (config.background) {
40885         // render grid on panel activation (if panel background)
40886         this.on('activate', function(gp) {
40887             if (!gp.grid.rendered) {
40888                 gp.grid.render(this.wrapper);
40889                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40890             }
40891         });
40892             
40893     } else {
40894         this.grid.render(this.wrapper);
40895         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40896
40897     }
40898     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40899     // ??? needed ??? config.el = this.wrapper;
40900     
40901     
40902     
40903   
40904     // xtype created footer. - not sure if will work as we normally have to render first..
40905     if (this.footer && !this.footer.el && this.footer.xtype) {
40906         
40907         var ctr = this.grid.getView().getFooterPanel(true);
40908         this.footer.dataSource = this.grid.dataSource;
40909         this.footer = Roo.factory(this.footer, Roo);
40910         this.footer.render(ctr);
40911         
40912     }
40913     
40914     
40915     
40916     
40917      
40918 };
40919
40920 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
40921 {
40922     // private
40923     is_resizing : false,
40924     
40925     getId : function(){
40926         return this.grid.id;
40927     },
40928     
40929     /**
40930      * Returns the grid for this panel
40931      * @return {Roo.bootstrap.Table} 
40932      */
40933     getGrid : function(){
40934         return this.grid;    
40935     },
40936     
40937     setSize : function(width, height)
40938     {
40939         if (this.is_resizing) {
40940             return;
40941         
40942         }
40943         this.is_resizing = true;
40944         if(!this.ignoreResize(width, height)){
40945             var grid = this.grid;
40946             var size = this.adjustForComponents(width, height);
40947             // tfoot is not a footer?
40948           
40949             
40950             var gridel = grid.getGridEl();
40951             gridel.setSize(size.width, size.height);
40952             
40953             var tbd = grid.getGridEl().select('tbody', true).first();
40954             var thd = grid.getGridEl().select('thead',true).first();
40955             var tbf= grid.getGridEl().select('tfoot', true).first();
40956
40957             if (tbf) {
40958                 size.height -= tbf.getHeight();
40959             }
40960             if (thd) {
40961                 size.height -= thd.getHeight();
40962             }
40963             
40964             tbd.setSize(size.width, size.height );
40965             // this is for the account management tab -seems to work there.
40966             var thd = grid.getGridEl().select('thead',true).first();
40967             //if (tbd) {
40968             //    tbd.setSize(size.width, size.height - thd.getHeight());
40969             //}
40970              
40971             grid.autoSize();
40972         }
40973         this.is_resizing = false;
40974     },
40975      
40976     
40977     
40978     beforeSlide : function(){
40979         this.grid.getView().scroller.clip();
40980     },
40981     
40982     afterSlide : function(){
40983         this.grid.getView().scroller.unclip();
40984     },
40985     
40986     destroy : function(){
40987         this.grid.destroy();
40988         delete this.grid;
40989         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40990     }
40991 });
40992
40993 /**
40994  * @class Roo.bootstrap.panel.Nest
40995  * @extends Roo.bootstrap.panel.Content
40996  * @constructor
40997  * Create a new Panel, that can contain a layout.Border.
40998  * 
40999  * 
41000  * @param {String/Object} config A string to set only the title or a config object
41001  */
41002 Roo.bootstrap.panel.Nest = function(config)
41003 {
41004     // construct with only one argument..
41005     /* FIXME - implement nicer consturctors
41006     if (layout.layout) {
41007         config = layout;
41008         layout = config.layout;
41009         delete config.layout;
41010     }
41011     if (layout.xtype && !layout.getEl) {
41012         // then layout needs constructing..
41013         layout = Roo.factory(layout, Roo);
41014     }
41015     */
41016     
41017     config.el =  config.layout.getEl();
41018     
41019     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41020     
41021     config.layout.monitorWindowResize = false; // turn off autosizing
41022     this.layout = config.layout;
41023     this.layout.getEl().addClass("roo-layout-nested-layout");
41024     this.layout.parent = this;
41025     
41026     
41027     
41028     
41029 };
41030
41031 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41032     /**
41033     * @cfg {Roo.BorderLayout} layout The layout for this panel
41034     */
41035     layout : false,
41036
41037     setSize : function(width, height){
41038         if(!this.ignoreResize(width, height)){
41039             var size = this.adjustForComponents(width, height);
41040             var el = this.layout.getEl();
41041             if (size.height < 1) {
41042                 el.setWidth(size.width);   
41043             } else {
41044                 el.setSize(size.width, size.height);
41045             }
41046             var touch = el.dom.offsetWidth;
41047             this.layout.layout();
41048             // ie requires a double layout on the first pass
41049             if(Roo.isIE && !this.initialized){
41050                 this.initialized = true;
41051                 this.layout.layout();
41052             }
41053         }
41054     },
41055     
41056     // activate all subpanels if not currently active..
41057     
41058     setActiveState : function(active){
41059         this.active = active;
41060         this.setActiveClass(active);
41061         
41062         if(!active){
41063             this.fireEvent("deactivate", this);
41064             return;
41065         }
41066         
41067         this.fireEvent("activate", this);
41068         // not sure if this should happen before or after..
41069         if (!this.layout) {
41070             return; // should not happen..
41071         }
41072         var reg = false;
41073         for (var r in this.layout.regions) {
41074             reg = this.layout.getRegion(r);
41075             if (reg.getActivePanel()) {
41076                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41077                 reg.setActivePanel(reg.getActivePanel());
41078                 continue;
41079             }
41080             if (!reg.panels.length) {
41081                 continue;
41082             }
41083             reg.showPanel(reg.getPanel(0));
41084         }
41085         
41086         
41087         
41088         
41089     },
41090     
41091     /**
41092      * Returns the nested BorderLayout for this panel
41093      * @return {Roo.BorderLayout} 
41094      */
41095     getLayout : function(){
41096         return this.layout;
41097     },
41098     
41099      /**
41100      * Adds a xtype elements to the layout of the nested panel
41101      * <pre><code>
41102
41103 panel.addxtype({
41104        xtype : 'ContentPanel',
41105        region: 'west',
41106        items: [ .... ]
41107    }
41108 );
41109
41110 panel.addxtype({
41111         xtype : 'NestedLayoutPanel',
41112         region: 'west',
41113         layout: {
41114            center: { },
41115            west: { }   
41116         },
41117         items : [ ... list of content panels or nested layout panels.. ]
41118    }
41119 );
41120 </code></pre>
41121      * @param {Object} cfg Xtype definition of item to add.
41122      */
41123     addxtype : function(cfg) {
41124         return this.layout.addxtype(cfg);
41125     
41126     }
41127 });/*
41128  * Based on:
41129  * Ext JS Library 1.1.1
41130  * Copyright(c) 2006-2007, Ext JS, LLC.
41131  *
41132  * Originally Released Under LGPL - original licence link has changed is not relivant.
41133  *
41134  * Fork - LGPL
41135  * <script type="text/javascript">
41136  */
41137 /**
41138  * @class Roo.TabPanel
41139  * @extends Roo.util.Observable
41140  * A lightweight tab container.
41141  * <br><br>
41142  * Usage:
41143  * <pre><code>
41144 // basic tabs 1, built from existing content
41145 var tabs = new Roo.TabPanel("tabs1");
41146 tabs.addTab("script", "View Script");
41147 tabs.addTab("markup", "View Markup");
41148 tabs.activate("script");
41149
41150 // more advanced tabs, built from javascript
41151 var jtabs = new Roo.TabPanel("jtabs");
41152 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41153
41154 // set up the UpdateManager
41155 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41156 var updater = tab2.getUpdateManager();
41157 updater.setDefaultUrl("ajax1.htm");
41158 tab2.on('activate', updater.refresh, updater, true);
41159
41160 // Use setUrl for Ajax loading
41161 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41162 tab3.setUrl("ajax2.htm", null, true);
41163
41164 // Disabled tab
41165 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41166 tab4.disable();
41167
41168 jtabs.activate("jtabs-1");
41169  * </code></pre>
41170  * @constructor
41171  * Create a new TabPanel.
41172  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41173  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41174  */
41175 Roo.bootstrap.panel.Tabs = function(config){
41176     /**
41177     * The container element for this TabPanel.
41178     * @type Roo.Element
41179     */
41180     this.el = Roo.get(config.el);
41181     delete config.el;
41182     if(config){
41183         if(typeof config == "boolean"){
41184             this.tabPosition = config ? "bottom" : "top";
41185         }else{
41186             Roo.apply(this, config);
41187         }
41188     }
41189     
41190     if(this.tabPosition == "bottom"){
41191         // if tabs are at the bottom = create the body first.
41192         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41193         this.el.addClass("roo-tabs-bottom");
41194     }
41195     // next create the tabs holders
41196     
41197     if (this.tabPosition == "west"){
41198         
41199         var reg = this.region; // fake it..
41200         while (reg) {
41201             if (!reg.mgr.parent) {
41202                 break;
41203             }
41204             reg = reg.mgr.parent.region;
41205         }
41206         Roo.log("got nest?");
41207         Roo.log(reg);
41208         if (reg.mgr.getRegion('west')) {
41209             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41210             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41211             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41212             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41213             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41214         
41215             
41216         }
41217         
41218         
41219     } else {
41220      
41221         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41222         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41223         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41224         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41225     }
41226     
41227     
41228     if(Roo.isIE){
41229         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41230     }
41231     
41232     // finally - if tabs are at the top, then create the body last..
41233     if(this.tabPosition != "bottom"){
41234         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41235          * @type Roo.Element
41236          */
41237         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41238         this.el.addClass("roo-tabs-top");
41239     }
41240     this.items = [];
41241
41242     this.bodyEl.setStyle("position", "relative");
41243
41244     this.active = null;
41245     this.activateDelegate = this.activate.createDelegate(this);
41246
41247     this.addEvents({
41248         /**
41249          * @event tabchange
41250          * Fires when the active tab changes
41251          * @param {Roo.TabPanel} this
41252          * @param {Roo.TabPanelItem} activePanel The new active tab
41253          */
41254         "tabchange": true,
41255         /**
41256          * @event beforetabchange
41257          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41258          * @param {Roo.TabPanel} this
41259          * @param {Object} e Set cancel to true on this object to cancel the tab change
41260          * @param {Roo.TabPanelItem} tab The tab being changed to
41261          */
41262         "beforetabchange" : true
41263     });
41264
41265     Roo.EventManager.onWindowResize(this.onResize, this);
41266     this.cpad = this.el.getPadding("lr");
41267     this.hiddenCount = 0;
41268
41269
41270     // toolbar on the tabbar support...
41271     if (this.toolbar) {
41272         alert("no toolbar support yet");
41273         this.toolbar  = false;
41274         /*
41275         var tcfg = this.toolbar;
41276         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41277         this.toolbar = new Roo.Toolbar(tcfg);
41278         if (Roo.isSafari) {
41279             var tbl = tcfg.container.child('table', true);
41280             tbl.setAttribute('width', '100%');
41281         }
41282         */
41283         
41284     }
41285    
41286
41287
41288     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41289 };
41290
41291 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41292     /*
41293      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41294      */
41295     tabPosition : "top",
41296     /*
41297      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41298      */
41299     currentTabWidth : 0,
41300     /*
41301      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41302      */
41303     minTabWidth : 40,
41304     /*
41305      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41306      */
41307     maxTabWidth : 250,
41308     /*
41309      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41310      */
41311     preferredTabWidth : 175,
41312     /*
41313      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41314      */
41315     resizeTabs : false,
41316     /*
41317      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41318      */
41319     monitorResize : true,
41320     /*
41321      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41322      */
41323     toolbar : false,  // set by caller..
41324     
41325     region : false, /// set by caller
41326     
41327     disableTooltips : true, // not used yet...
41328
41329     /**
41330      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41331      * @param {String} id The id of the div to use <b>or create</b>
41332      * @param {String} text The text for the tab
41333      * @param {String} content (optional) Content to put in the TabPanelItem body
41334      * @param {Boolean} closable (optional) True to create a close icon on the tab
41335      * @return {Roo.TabPanelItem} The created TabPanelItem
41336      */
41337     addTab : function(id, text, content, closable, tpl)
41338     {
41339         var item = new Roo.bootstrap.panel.TabItem({
41340             panel: this,
41341             id : id,
41342             text : text,
41343             closable : closable,
41344             tpl : tpl
41345         });
41346         this.addTabItem(item);
41347         if(content){
41348             item.setContent(content);
41349         }
41350         return item;
41351     },
41352
41353     /**
41354      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41355      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41356      * @return {Roo.TabPanelItem}
41357      */
41358     getTab : function(id){
41359         return this.items[id];
41360     },
41361
41362     /**
41363      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41364      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41365      */
41366     hideTab : function(id){
41367         var t = this.items[id];
41368         if(!t.isHidden()){
41369            t.setHidden(true);
41370            this.hiddenCount++;
41371            this.autoSizeTabs();
41372         }
41373     },
41374
41375     /**
41376      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41377      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41378      */
41379     unhideTab : function(id){
41380         var t = this.items[id];
41381         if(t.isHidden()){
41382            t.setHidden(false);
41383            this.hiddenCount--;
41384            this.autoSizeTabs();
41385         }
41386     },
41387
41388     /**
41389      * Adds an existing {@link Roo.TabPanelItem}.
41390      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41391      */
41392     addTabItem : function(item)
41393     {
41394         this.items[item.id] = item;
41395         this.items.push(item);
41396         this.autoSizeTabs();
41397       //  if(this.resizeTabs){
41398     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41399   //         this.autoSizeTabs();
41400 //        }else{
41401 //            item.autoSize();
41402        // }
41403     },
41404
41405     /**
41406      * Removes a {@link Roo.TabPanelItem}.
41407      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41408      */
41409     removeTab : function(id){
41410         var items = this.items;
41411         var tab = items[id];
41412         if(!tab) { return; }
41413         var index = items.indexOf(tab);
41414         if(this.active == tab && items.length > 1){
41415             var newTab = this.getNextAvailable(index);
41416             if(newTab) {
41417                 newTab.activate();
41418             }
41419         }
41420         this.stripEl.dom.removeChild(tab.pnode.dom);
41421         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41422             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41423         }
41424         items.splice(index, 1);
41425         delete this.items[tab.id];
41426         tab.fireEvent("close", tab);
41427         tab.purgeListeners();
41428         this.autoSizeTabs();
41429     },
41430
41431     getNextAvailable : function(start){
41432         var items = this.items;
41433         var index = start;
41434         // look for a next tab that will slide over to
41435         // replace the one being removed
41436         while(index < items.length){
41437             var item = items[++index];
41438             if(item && !item.isHidden()){
41439                 return item;
41440             }
41441         }
41442         // if one isn't found select the previous tab (on the left)
41443         index = start;
41444         while(index >= 0){
41445             var item = items[--index];
41446             if(item && !item.isHidden()){
41447                 return item;
41448             }
41449         }
41450         return null;
41451     },
41452
41453     /**
41454      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41455      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41456      */
41457     disableTab : function(id){
41458         var tab = this.items[id];
41459         if(tab && this.active != tab){
41460             tab.disable();
41461         }
41462     },
41463
41464     /**
41465      * Enables a {@link Roo.TabPanelItem} that is disabled.
41466      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41467      */
41468     enableTab : function(id){
41469         var tab = this.items[id];
41470         tab.enable();
41471     },
41472
41473     /**
41474      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41475      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41476      * @return {Roo.TabPanelItem} The TabPanelItem.
41477      */
41478     activate : function(id)
41479     {
41480         //Roo.log('activite:'  + id);
41481         
41482         var tab = this.items[id];
41483         if(!tab){
41484             return null;
41485         }
41486         if(tab == this.active || tab.disabled){
41487             return tab;
41488         }
41489         var e = {};
41490         this.fireEvent("beforetabchange", this, e, tab);
41491         if(e.cancel !== true && !tab.disabled){
41492             if(this.active){
41493                 this.active.hide();
41494             }
41495             this.active = this.items[id];
41496             this.active.show();
41497             this.fireEvent("tabchange", this, this.active);
41498         }
41499         return tab;
41500     },
41501
41502     /**
41503      * Gets the active {@link Roo.TabPanelItem}.
41504      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41505      */
41506     getActiveTab : function(){
41507         return this.active;
41508     },
41509
41510     /**
41511      * Updates the tab body element to fit the height of the container element
41512      * for overflow scrolling
41513      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41514      */
41515     syncHeight : function(targetHeight){
41516         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41517         var bm = this.bodyEl.getMargins();
41518         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41519         this.bodyEl.setHeight(newHeight);
41520         return newHeight;
41521     },
41522
41523     onResize : function(){
41524         if(this.monitorResize){
41525             this.autoSizeTabs();
41526         }
41527     },
41528
41529     /**
41530      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41531      */
41532     beginUpdate : function(){
41533         this.updating = true;
41534     },
41535
41536     /**
41537      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41538      */
41539     endUpdate : function(){
41540         this.updating = false;
41541         this.autoSizeTabs();
41542     },
41543
41544     /**
41545      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41546      */
41547     autoSizeTabs : function()
41548     {
41549         var count = this.items.length;
41550         var vcount = count - this.hiddenCount;
41551         
41552         if (vcount < 2) {
41553             this.stripEl.hide();
41554         } else {
41555             this.stripEl.show();
41556         }
41557         
41558         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41559             return;
41560         }
41561         
41562         
41563         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41564         var availWidth = Math.floor(w / vcount);
41565         var b = this.stripBody;
41566         if(b.getWidth() > w){
41567             var tabs = this.items;
41568             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41569             if(availWidth < this.minTabWidth){
41570                 /*if(!this.sleft){    // incomplete scrolling code
41571                     this.createScrollButtons();
41572                 }
41573                 this.showScroll();
41574                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41575             }
41576         }else{
41577             if(this.currentTabWidth < this.preferredTabWidth){
41578                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41579             }
41580         }
41581     },
41582
41583     /**
41584      * Returns the number of tabs in this TabPanel.
41585      * @return {Number}
41586      */
41587      getCount : function(){
41588          return this.items.length;
41589      },
41590
41591     /**
41592      * Resizes all the tabs to the passed width
41593      * @param {Number} The new width
41594      */
41595     setTabWidth : function(width){
41596         this.currentTabWidth = width;
41597         for(var i = 0, len = this.items.length; i < len; i++) {
41598                 if(!this.items[i].isHidden()) {
41599                 this.items[i].setWidth(width);
41600             }
41601         }
41602     },
41603
41604     /**
41605      * Destroys this TabPanel
41606      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41607      */
41608     destroy : function(removeEl){
41609         Roo.EventManager.removeResizeListener(this.onResize, this);
41610         for(var i = 0, len = this.items.length; i < len; i++){
41611             this.items[i].purgeListeners();
41612         }
41613         if(removeEl === true){
41614             this.el.update("");
41615             this.el.remove();
41616         }
41617     },
41618     
41619     createStrip : function(container)
41620     {
41621         var strip = document.createElement("nav");
41622         strip.className = Roo.bootstrap.version == 4 ?
41623             "navbar-light bg-light" : 
41624             "navbar navbar-default"; //"x-tabs-wrap";
41625         container.appendChild(strip);
41626         return strip;
41627     },
41628     
41629     createStripList : function(strip)
41630     {
41631         // div wrapper for retard IE
41632         // returns the "tr" element.
41633         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41634         //'<div class="x-tabs-strip-wrap">'+
41635           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41636           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41637         return strip.firstChild; //.firstChild.firstChild.firstChild;
41638     },
41639     createBody : function(container)
41640     {
41641         var body = document.createElement("div");
41642         Roo.id(body, "tab-body");
41643         //Roo.fly(body).addClass("x-tabs-body");
41644         Roo.fly(body).addClass("tab-content");
41645         container.appendChild(body);
41646         return body;
41647     },
41648     createItemBody :function(bodyEl, id){
41649         var body = Roo.getDom(id);
41650         if(!body){
41651             body = document.createElement("div");
41652             body.id = id;
41653         }
41654         //Roo.fly(body).addClass("x-tabs-item-body");
41655         Roo.fly(body).addClass("tab-pane");
41656          bodyEl.insertBefore(body, bodyEl.firstChild);
41657         return body;
41658     },
41659     /** @private */
41660     createStripElements :  function(stripEl, text, closable, tpl)
41661     {
41662         var td = document.createElement("li"); // was td..
41663         td.className = 'nav-item';
41664         
41665         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41666         
41667         
41668         stripEl.appendChild(td);
41669         /*if(closable){
41670             td.className = "x-tabs-closable";
41671             if(!this.closeTpl){
41672                 this.closeTpl = new Roo.Template(
41673                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41674                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41675                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41676                 );
41677             }
41678             var el = this.closeTpl.overwrite(td, {"text": text});
41679             var close = el.getElementsByTagName("div")[0];
41680             var inner = el.getElementsByTagName("em")[0];
41681             return {"el": el, "close": close, "inner": inner};
41682         } else {
41683         */
41684         // not sure what this is..
41685 //            if(!this.tabTpl){
41686                 //this.tabTpl = new Roo.Template(
41687                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41688                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41689                 //);
41690 //                this.tabTpl = new Roo.Template(
41691 //                   '<a href="#">' +
41692 //                   '<span unselectable="on"' +
41693 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41694 //                            ' >{text}</span></a>'
41695 //                );
41696 //                
41697 //            }
41698
41699
41700             var template = tpl || this.tabTpl || false;
41701             
41702             if(!template){
41703                 template =  new Roo.Template(
41704                         Roo.bootstrap.version == 4 ? 
41705                             (
41706                                 '<a class="nav-link" href="#" unselectable="on"' +
41707                                      (this.disableTooltips ? '' : ' title="{text}"') +
41708                                      ' >{text}</a>'
41709                             ) : (
41710                                 '<a class="nav-link" href="#">' +
41711                                 '<span unselectable="on"' +
41712                                          (this.disableTooltips ? '' : ' title="{text}"') +
41713                                     ' >{text}</span></a>'
41714                             )
41715                 );
41716             }
41717             
41718             switch (typeof(template)) {
41719                 case 'object' :
41720                     break;
41721                 case 'string' :
41722                     template = new Roo.Template(template);
41723                     break;
41724                 default :
41725                     break;
41726             }
41727             
41728             var el = template.overwrite(td, {"text": text});
41729             
41730             var inner = el.getElementsByTagName("span")[0];
41731             
41732             return {"el": el, "inner": inner};
41733             
41734     }
41735         
41736     
41737 });
41738
41739 /**
41740  * @class Roo.TabPanelItem
41741  * @extends Roo.util.Observable
41742  * Represents an individual item (tab plus body) in a TabPanel.
41743  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41744  * @param {String} id The id of this TabPanelItem
41745  * @param {String} text The text for the tab of this TabPanelItem
41746  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41747  */
41748 Roo.bootstrap.panel.TabItem = function(config){
41749     /**
41750      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41751      * @type Roo.TabPanel
41752      */
41753     this.tabPanel = config.panel;
41754     /**
41755      * The id for this TabPanelItem
41756      * @type String
41757      */
41758     this.id = config.id;
41759     /** @private */
41760     this.disabled = false;
41761     /** @private */
41762     this.text = config.text;
41763     /** @private */
41764     this.loaded = false;
41765     this.closable = config.closable;
41766
41767     /**
41768      * The body element for this TabPanelItem.
41769      * @type Roo.Element
41770      */
41771     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41772     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41773     this.bodyEl.setStyle("display", "block");
41774     this.bodyEl.setStyle("zoom", "1");
41775     //this.hideAction();
41776
41777     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41778     /** @private */
41779     this.el = Roo.get(els.el);
41780     this.inner = Roo.get(els.inner, true);
41781      this.textEl = Roo.bootstrap.version == 4 ?
41782         this.el : Roo.get(this.el.dom.firstChild, true);
41783
41784     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41785     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41786
41787     
41788 //    this.el.on("mousedown", this.onTabMouseDown, this);
41789     this.el.on("click", this.onTabClick, this);
41790     /** @private */
41791     if(config.closable){
41792         var c = Roo.get(els.close, true);
41793         c.dom.title = this.closeText;
41794         c.addClassOnOver("close-over");
41795         c.on("click", this.closeClick, this);
41796      }
41797
41798     this.addEvents({
41799          /**
41800          * @event activate
41801          * Fires when this tab becomes the active tab.
41802          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41803          * @param {Roo.TabPanelItem} this
41804          */
41805         "activate": true,
41806         /**
41807          * @event beforeclose
41808          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41809          * @param {Roo.TabPanelItem} this
41810          * @param {Object} e Set cancel to true on this object to cancel the close.
41811          */
41812         "beforeclose": true,
41813         /**
41814          * @event close
41815          * Fires when this tab is closed.
41816          * @param {Roo.TabPanelItem} this
41817          */
41818          "close": true,
41819         /**
41820          * @event deactivate
41821          * Fires when this tab is no longer the active tab.
41822          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41823          * @param {Roo.TabPanelItem} this
41824          */
41825          "deactivate" : true
41826     });
41827     this.hidden = false;
41828
41829     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41830 };
41831
41832 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41833            {
41834     purgeListeners : function(){
41835        Roo.util.Observable.prototype.purgeListeners.call(this);
41836        this.el.removeAllListeners();
41837     },
41838     /**
41839      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41840      */
41841     show : function(){
41842         this.status_node.addClass("active");
41843         this.showAction();
41844         if(Roo.isOpera){
41845             this.tabPanel.stripWrap.repaint();
41846         }
41847         this.fireEvent("activate", this.tabPanel, this);
41848     },
41849
41850     /**
41851      * Returns true if this tab is the active tab.
41852      * @return {Boolean}
41853      */
41854     isActive : function(){
41855         return this.tabPanel.getActiveTab() == this;
41856     },
41857
41858     /**
41859      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41860      */
41861     hide : function(){
41862         this.status_node.removeClass("active");
41863         this.hideAction();
41864         this.fireEvent("deactivate", this.tabPanel, this);
41865     },
41866
41867     hideAction : function(){
41868         this.bodyEl.hide();
41869         this.bodyEl.setStyle("position", "absolute");
41870         this.bodyEl.setLeft("-20000px");
41871         this.bodyEl.setTop("-20000px");
41872     },
41873
41874     showAction : function(){
41875         this.bodyEl.setStyle("position", "relative");
41876         this.bodyEl.setTop("");
41877         this.bodyEl.setLeft("");
41878         this.bodyEl.show();
41879     },
41880
41881     /**
41882      * Set the tooltip for the tab.
41883      * @param {String} tooltip The tab's tooltip
41884      */
41885     setTooltip : function(text){
41886         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41887             this.textEl.dom.qtip = text;
41888             this.textEl.dom.removeAttribute('title');
41889         }else{
41890             this.textEl.dom.title = text;
41891         }
41892     },
41893
41894     onTabClick : function(e){
41895         e.preventDefault();
41896         this.tabPanel.activate(this.id);
41897     },
41898
41899     onTabMouseDown : function(e){
41900         e.preventDefault();
41901         this.tabPanel.activate(this.id);
41902     },
41903 /*
41904     getWidth : function(){
41905         return this.inner.getWidth();
41906     },
41907
41908     setWidth : function(width){
41909         var iwidth = width - this.linode.getPadding("lr");
41910         this.inner.setWidth(iwidth);
41911         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41912         this.linode.setWidth(width);
41913     },
41914 */
41915     /**
41916      * Show or hide the tab
41917      * @param {Boolean} hidden True to hide or false to show.
41918      */
41919     setHidden : function(hidden){
41920         this.hidden = hidden;
41921         this.linode.setStyle("display", hidden ? "none" : "");
41922     },
41923
41924     /**
41925      * Returns true if this tab is "hidden"
41926      * @return {Boolean}
41927      */
41928     isHidden : function(){
41929         return this.hidden;
41930     },
41931
41932     /**
41933      * Returns the text for this tab
41934      * @return {String}
41935      */
41936     getText : function(){
41937         return this.text;
41938     },
41939     /*
41940     autoSize : function(){
41941         //this.el.beginMeasure();
41942         this.textEl.setWidth(1);
41943         /*
41944          *  #2804 [new] Tabs in Roojs
41945          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41946          */
41947         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41948         //this.el.endMeasure();
41949     //},
41950
41951     /**
41952      * Sets the text for the tab (Note: this also sets the tooltip text)
41953      * @param {String} text The tab's text and tooltip
41954      */
41955     setText : function(text){
41956         this.text = text;
41957         this.textEl.update(text);
41958         this.setTooltip(text);
41959         //if(!this.tabPanel.resizeTabs){
41960         //    this.autoSize();
41961         //}
41962     },
41963     /**
41964      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41965      */
41966     activate : function(){
41967         this.tabPanel.activate(this.id);
41968     },
41969
41970     /**
41971      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41972      */
41973     disable : function(){
41974         if(this.tabPanel.active != this){
41975             this.disabled = true;
41976             this.status_node.addClass("disabled");
41977         }
41978     },
41979
41980     /**
41981      * Enables this TabPanelItem if it was previously disabled.
41982      */
41983     enable : function(){
41984         this.disabled = false;
41985         this.status_node.removeClass("disabled");
41986     },
41987
41988     /**
41989      * Sets the content for this TabPanelItem.
41990      * @param {String} content The content
41991      * @param {Boolean} loadScripts true to look for and load scripts
41992      */
41993     setContent : function(content, loadScripts){
41994         this.bodyEl.update(content, loadScripts);
41995     },
41996
41997     /**
41998      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41999      * @return {Roo.UpdateManager} The UpdateManager
42000      */
42001     getUpdateManager : function(){
42002         return this.bodyEl.getUpdateManager();
42003     },
42004
42005     /**
42006      * Set a URL to be used to load the content for this TabPanelItem.
42007      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42008      * @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)
42009      * @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)
42010      * @return {Roo.UpdateManager} The UpdateManager
42011      */
42012     setUrl : function(url, params, loadOnce){
42013         if(this.refreshDelegate){
42014             this.un('activate', this.refreshDelegate);
42015         }
42016         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42017         this.on("activate", this.refreshDelegate);
42018         return this.bodyEl.getUpdateManager();
42019     },
42020
42021     /** @private */
42022     _handleRefresh : function(url, params, loadOnce){
42023         if(!loadOnce || !this.loaded){
42024             var updater = this.bodyEl.getUpdateManager();
42025             updater.update(url, params, this._setLoaded.createDelegate(this));
42026         }
42027     },
42028
42029     /**
42030      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42031      *   Will fail silently if the setUrl method has not been called.
42032      *   This does not activate the panel, just updates its content.
42033      */
42034     refresh : function(){
42035         if(this.refreshDelegate){
42036            this.loaded = false;
42037            this.refreshDelegate();
42038         }
42039     },
42040
42041     /** @private */
42042     _setLoaded : function(){
42043         this.loaded = true;
42044     },
42045
42046     /** @private */
42047     closeClick : function(e){
42048         var o = {};
42049         e.stopEvent();
42050         this.fireEvent("beforeclose", this, o);
42051         if(o.cancel !== true){
42052             this.tabPanel.removeTab(this.id);
42053         }
42054     },
42055     /**
42056      * The text displayed in the tooltip for the close icon.
42057      * @type String
42058      */
42059     closeText : "Close this tab"
42060 });
42061 /**
42062 *    This script refer to:
42063 *    Title: International Telephone Input
42064 *    Author: Jack O'Connor
42065 *    Code version:  v12.1.12
42066 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42067 **/
42068
42069 Roo.bootstrap.form.PhoneInputData = function() {
42070     var d = [
42071       [
42072         "Afghanistan (‫افغانستان‬‎)",
42073         "af",
42074         "93"
42075       ],
42076       [
42077         "Albania (Shqipëri)",
42078         "al",
42079         "355"
42080       ],
42081       [
42082         "Algeria (‫الجزائر‬‎)",
42083         "dz",
42084         "213"
42085       ],
42086       [
42087         "American Samoa",
42088         "as",
42089         "1684"
42090       ],
42091       [
42092         "Andorra",
42093         "ad",
42094         "376"
42095       ],
42096       [
42097         "Angola",
42098         "ao",
42099         "244"
42100       ],
42101       [
42102         "Anguilla",
42103         "ai",
42104         "1264"
42105       ],
42106       [
42107         "Antigua and Barbuda",
42108         "ag",
42109         "1268"
42110       ],
42111       [
42112         "Argentina",
42113         "ar",
42114         "54"
42115       ],
42116       [
42117         "Armenia (Հայաստան)",
42118         "am",
42119         "374"
42120       ],
42121       [
42122         "Aruba",
42123         "aw",
42124         "297"
42125       ],
42126       [
42127         "Australia",
42128         "au",
42129         "61",
42130         0
42131       ],
42132       [
42133         "Austria (Österreich)",
42134         "at",
42135         "43"
42136       ],
42137       [
42138         "Azerbaijan (Azərbaycan)",
42139         "az",
42140         "994"
42141       ],
42142       [
42143         "Bahamas",
42144         "bs",
42145         "1242"
42146       ],
42147       [
42148         "Bahrain (‫البحرين‬‎)",
42149         "bh",
42150         "973"
42151       ],
42152       [
42153         "Bangladesh (বাংলাদেশ)",
42154         "bd",
42155         "880"
42156       ],
42157       [
42158         "Barbados",
42159         "bb",
42160         "1246"
42161       ],
42162       [
42163         "Belarus (Беларусь)",
42164         "by",
42165         "375"
42166       ],
42167       [
42168         "Belgium (België)",
42169         "be",
42170         "32"
42171       ],
42172       [
42173         "Belize",
42174         "bz",
42175         "501"
42176       ],
42177       [
42178         "Benin (Bénin)",
42179         "bj",
42180         "229"
42181       ],
42182       [
42183         "Bermuda",
42184         "bm",
42185         "1441"
42186       ],
42187       [
42188         "Bhutan (འབྲུག)",
42189         "bt",
42190         "975"
42191       ],
42192       [
42193         "Bolivia",
42194         "bo",
42195         "591"
42196       ],
42197       [
42198         "Bosnia and Herzegovina (Босна и Херцеговина)",
42199         "ba",
42200         "387"
42201       ],
42202       [
42203         "Botswana",
42204         "bw",
42205         "267"
42206       ],
42207       [
42208         "Brazil (Brasil)",
42209         "br",
42210         "55"
42211       ],
42212       [
42213         "British Indian Ocean Territory",
42214         "io",
42215         "246"
42216       ],
42217       [
42218         "British Virgin Islands",
42219         "vg",
42220         "1284"
42221       ],
42222       [
42223         "Brunei",
42224         "bn",
42225         "673"
42226       ],
42227       [
42228         "Bulgaria (България)",
42229         "bg",
42230         "359"
42231       ],
42232       [
42233         "Burkina Faso",
42234         "bf",
42235         "226"
42236       ],
42237       [
42238         "Burundi (Uburundi)",
42239         "bi",
42240         "257"
42241       ],
42242       [
42243         "Cambodia (កម្ពុជា)",
42244         "kh",
42245         "855"
42246       ],
42247       [
42248         "Cameroon (Cameroun)",
42249         "cm",
42250         "237"
42251       ],
42252       [
42253         "Canada",
42254         "ca",
42255         "1",
42256         1,
42257         ["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"]
42258       ],
42259       [
42260         "Cape Verde (Kabu Verdi)",
42261         "cv",
42262         "238"
42263       ],
42264       [
42265         "Caribbean Netherlands",
42266         "bq",
42267         "599",
42268         1
42269       ],
42270       [
42271         "Cayman Islands",
42272         "ky",
42273         "1345"
42274       ],
42275       [
42276         "Central African Republic (République centrafricaine)",
42277         "cf",
42278         "236"
42279       ],
42280       [
42281         "Chad (Tchad)",
42282         "td",
42283         "235"
42284       ],
42285       [
42286         "Chile",
42287         "cl",
42288         "56"
42289       ],
42290       [
42291         "China (中国)",
42292         "cn",
42293         "86"
42294       ],
42295       [
42296         "Christmas Island",
42297         "cx",
42298         "61",
42299         2
42300       ],
42301       [
42302         "Cocos (Keeling) Islands",
42303         "cc",
42304         "61",
42305         1
42306       ],
42307       [
42308         "Colombia",
42309         "co",
42310         "57"
42311       ],
42312       [
42313         "Comoros (‫جزر القمر‬‎)",
42314         "km",
42315         "269"
42316       ],
42317       [
42318         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42319         "cd",
42320         "243"
42321       ],
42322       [
42323         "Congo (Republic) (Congo-Brazzaville)",
42324         "cg",
42325         "242"
42326       ],
42327       [
42328         "Cook Islands",
42329         "ck",
42330         "682"
42331       ],
42332       [
42333         "Costa Rica",
42334         "cr",
42335         "506"
42336       ],
42337       [
42338         "Côte d’Ivoire",
42339         "ci",
42340         "225"
42341       ],
42342       [
42343         "Croatia (Hrvatska)",
42344         "hr",
42345         "385"
42346       ],
42347       [
42348         "Cuba",
42349         "cu",
42350         "53"
42351       ],
42352       [
42353         "Curaçao",
42354         "cw",
42355         "599",
42356         0
42357       ],
42358       [
42359         "Cyprus (Κύπρος)",
42360         "cy",
42361         "357"
42362       ],
42363       [
42364         "Czech Republic (Česká republika)",
42365         "cz",
42366         "420"
42367       ],
42368       [
42369         "Denmark (Danmark)",
42370         "dk",
42371         "45"
42372       ],
42373       [
42374         "Djibouti",
42375         "dj",
42376         "253"
42377       ],
42378       [
42379         "Dominica",
42380         "dm",
42381         "1767"
42382       ],
42383       [
42384         "Dominican Republic (República Dominicana)",
42385         "do",
42386         "1",
42387         2,
42388         ["809", "829", "849"]
42389       ],
42390       [
42391         "Ecuador",
42392         "ec",
42393         "593"
42394       ],
42395       [
42396         "Egypt (‫مصر‬‎)",
42397         "eg",
42398         "20"
42399       ],
42400       [
42401         "El Salvador",
42402         "sv",
42403         "503"
42404       ],
42405       [
42406         "Equatorial Guinea (Guinea Ecuatorial)",
42407         "gq",
42408         "240"
42409       ],
42410       [
42411         "Eritrea",
42412         "er",
42413         "291"
42414       ],
42415       [
42416         "Estonia (Eesti)",
42417         "ee",
42418         "372"
42419       ],
42420       [
42421         "Ethiopia",
42422         "et",
42423         "251"
42424       ],
42425       [
42426         "Falkland Islands (Islas Malvinas)",
42427         "fk",
42428         "500"
42429       ],
42430       [
42431         "Faroe Islands (Føroyar)",
42432         "fo",
42433         "298"
42434       ],
42435       [
42436         "Fiji",
42437         "fj",
42438         "679"
42439       ],
42440       [
42441         "Finland (Suomi)",
42442         "fi",
42443         "358",
42444         0
42445       ],
42446       [
42447         "France",
42448         "fr",
42449         "33"
42450       ],
42451       [
42452         "French Guiana (Guyane française)",
42453         "gf",
42454         "594"
42455       ],
42456       [
42457         "French Polynesia (Polynésie française)",
42458         "pf",
42459         "689"
42460       ],
42461       [
42462         "Gabon",
42463         "ga",
42464         "241"
42465       ],
42466       [
42467         "Gambia",
42468         "gm",
42469         "220"
42470       ],
42471       [
42472         "Georgia (საქართველო)",
42473         "ge",
42474         "995"
42475       ],
42476       [
42477         "Germany (Deutschland)",
42478         "de",
42479         "49"
42480       ],
42481       [
42482         "Ghana (Gaana)",
42483         "gh",
42484         "233"
42485       ],
42486       [
42487         "Gibraltar",
42488         "gi",
42489         "350"
42490       ],
42491       [
42492         "Greece (Ελλάδα)",
42493         "gr",
42494         "30"
42495       ],
42496       [
42497         "Greenland (Kalaallit Nunaat)",
42498         "gl",
42499         "299"
42500       ],
42501       [
42502         "Grenada",
42503         "gd",
42504         "1473"
42505       ],
42506       [
42507         "Guadeloupe",
42508         "gp",
42509         "590",
42510         0
42511       ],
42512       [
42513         "Guam",
42514         "gu",
42515         "1671"
42516       ],
42517       [
42518         "Guatemala",
42519         "gt",
42520         "502"
42521       ],
42522       [
42523         "Guernsey",
42524         "gg",
42525         "44",
42526         1
42527       ],
42528       [
42529         "Guinea (Guinée)",
42530         "gn",
42531         "224"
42532       ],
42533       [
42534         "Guinea-Bissau (Guiné Bissau)",
42535         "gw",
42536         "245"
42537       ],
42538       [
42539         "Guyana",
42540         "gy",
42541         "592"
42542       ],
42543       [
42544         "Haiti",
42545         "ht",
42546         "509"
42547       ],
42548       [
42549         "Honduras",
42550         "hn",
42551         "504"
42552       ],
42553       [
42554         "Hong Kong (香港)",
42555         "hk",
42556         "852"
42557       ],
42558       [
42559         "Hungary (Magyarország)",
42560         "hu",
42561         "36"
42562       ],
42563       [
42564         "Iceland (Ísland)",
42565         "is",
42566         "354"
42567       ],
42568       [
42569         "India (भारत)",
42570         "in",
42571         "91"
42572       ],
42573       [
42574         "Indonesia",
42575         "id",
42576         "62"
42577       ],
42578       [
42579         "Iran (‫ایران‬‎)",
42580         "ir",
42581         "98"
42582       ],
42583       [
42584         "Iraq (‫العراق‬‎)",
42585         "iq",
42586         "964"
42587       ],
42588       [
42589         "Ireland",
42590         "ie",
42591         "353"
42592       ],
42593       [
42594         "Isle of Man",
42595         "im",
42596         "44",
42597         2
42598       ],
42599       [
42600         "Israel (‫ישראל‬‎)",
42601         "il",
42602         "972"
42603       ],
42604       [
42605         "Italy (Italia)",
42606         "it",
42607         "39",
42608         0
42609       ],
42610       [
42611         "Jamaica",
42612         "jm",
42613         "1876"
42614       ],
42615       [
42616         "Japan (日本)",
42617         "jp",
42618         "81"
42619       ],
42620       [
42621         "Jersey",
42622         "je",
42623         "44",
42624         3
42625       ],
42626       [
42627         "Jordan (‫الأردن‬‎)",
42628         "jo",
42629         "962"
42630       ],
42631       [
42632         "Kazakhstan (Казахстан)",
42633         "kz",
42634         "7",
42635         1
42636       ],
42637       [
42638         "Kenya",
42639         "ke",
42640         "254"
42641       ],
42642       [
42643         "Kiribati",
42644         "ki",
42645         "686"
42646       ],
42647       [
42648         "Kosovo",
42649         "xk",
42650         "383"
42651       ],
42652       [
42653         "Kuwait (‫الكويت‬‎)",
42654         "kw",
42655         "965"
42656       ],
42657       [
42658         "Kyrgyzstan (Кыргызстан)",
42659         "kg",
42660         "996"
42661       ],
42662       [
42663         "Laos (ລາວ)",
42664         "la",
42665         "856"
42666       ],
42667       [
42668         "Latvia (Latvija)",
42669         "lv",
42670         "371"
42671       ],
42672       [
42673         "Lebanon (‫لبنان‬‎)",
42674         "lb",
42675         "961"
42676       ],
42677       [
42678         "Lesotho",
42679         "ls",
42680         "266"
42681       ],
42682       [
42683         "Liberia",
42684         "lr",
42685         "231"
42686       ],
42687       [
42688         "Libya (‫ليبيا‬‎)",
42689         "ly",
42690         "218"
42691       ],
42692       [
42693         "Liechtenstein",
42694         "li",
42695         "423"
42696       ],
42697       [
42698         "Lithuania (Lietuva)",
42699         "lt",
42700         "370"
42701       ],
42702       [
42703         "Luxembourg",
42704         "lu",
42705         "352"
42706       ],
42707       [
42708         "Macau (澳門)",
42709         "mo",
42710         "853"
42711       ],
42712       [
42713         "Macedonia (FYROM) (Македонија)",
42714         "mk",
42715         "389"
42716       ],
42717       [
42718         "Madagascar (Madagasikara)",
42719         "mg",
42720         "261"
42721       ],
42722       [
42723         "Malawi",
42724         "mw",
42725         "265"
42726       ],
42727       [
42728         "Malaysia",
42729         "my",
42730         "60"
42731       ],
42732       [
42733         "Maldives",
42734         "mv",
42735         "960"
42736       ],
42737       [
42738         "Mali",
42739         "ml",
42740         "223"
42741       ],
42742       [
42743         "Malta",
42744         "mt",
42745         "356"
42746       ],
42747       [
42748         "Marshall Islands",
42749         "mh",
42750         "692"
42751       ],
42752       [
42753         "Martinique",
42754         "mq",
42755         "596"
42756       ],
42757       [
42758         "Mauritania (‫موريتانيا‬‎)",
42759         "mr",
42760         "222"
42761       ],
42762       [
42763         "Mauritius (Moris)",
42764         "mu",
42765         "230"
42766       ],
42767       [
42768         "Mayotte",
42769         "yt",
42770         "262",
42771         1
42772       ],
42773       [
42774         "Mexico (México)",
42775         "mx",
42776         "52"
42777       ],
42778       [
42779         "Micronesia",
42780         "fm",
42781         "691"
42782       ],
42783       [
42784         "Moldova (Republica Moldova)",
42785         "md",
42786         "373"
42787       ],
42788       [
42789         "Monaco",
42790         "mc",
42791         "377"
42792       ],
42793       [
42794         "Mongolia (Монгол)",
42795         "mn",
42796         "976"
42797       ],
42798       [
42799         "Montenegro (Crna Gora)",
42800         "me",
42801         "382"
42802       ],
42803       [
42804         "Montserrat",
42805         "ms",
42806         "1664"
42807       ],
42808       [
42809         "Morocco (‫المغرب‬‎)",
42810         "ma",
42811         "212",
42812         0
42813       ],
42814       [
42815         "Mozambique (Moçambique)",
42816         "mz",
42817         "258"
42818       ],
42819       [
42820         "Myanmar (Burma) (မြန်မာ)",
42821         "mm",
42822         "95"
42823       ],
42824       [
42825         "Namibia (Namibië)",
42826         "na",
42827         "264"
42828       ],
42829       [
42830         "Nauru",
42831         "nr",
42832         "674"
42833       ],
42834       [
42835         "Nepal (नेपाल)",
42836         "np",
42837         "977"
42838       ],
42839       [
42840         "Netherlands (Nederland)",
42841         "nl",
42842         "31"
42843       ],
42844       [
42845         "New Caledonia (Nouvelle-Calédonie)",
42846         "nc",
42847         "687"
42848       ],
42849       [
42850         "New Zealand",
42851         "nz",
42852         "64"
42853       ],
42854       [
42855         "Nicaragua",
42856         "ni",
42857         "505"
42858       ],
42859       [
42860         "Niger (Nijar)",
42861         "ne",
42862         "227"
42863       ],
42864       [
42865         "Nigeria",
42866         "ng",
42867         "234"
42868       ],
42869       [
42870         "Niue",
42871         "nu",
42872         "683"
42873       ],
42874       [
42875         "Norfolk Island",
42876         "nf",
42877         "672"
42878       ],
42879       [
42880         "North Korea (조선 민주주의 인민 공화국)",
42881         "kp",
42882         "850"
42883       ],
42884       [
42885         "Northern Mariana Islands",
42886         "mp",
42887         "1670"
42888       ],
42889       [
42890         "Norway (Norge)",
42891         "no",
42892         "47",
42893         0
42894       ],
42895       [
42896         "Oman (‫عُمان‬‎)",
42897         "om",
42898         "968"
42899       ],
42900       [
42901         "Pakistan (‫پاکستان‬‎)",
42902         "pk",
42903         "92"
42904       ],
42905       [
42906         "Palau",
42907         "pw",
42908         "680"
42909       ],
42910       [
42911         "Palestine (‫فلسطين‬‎)",
42912         "ps",
42913         "970"
42914       ],
42915       [
42916         "Panama (Panamá)",
42917         "pa",
42918         "507"
42919       ],
42920       [
42921         "Papua New Guinea",
42922         "pg",
42923         "675"
42924       ],
42925       [
42926         "Paraguay",
42927         "py",
42928         "595"
42929       ],
42930       [
42931         "Peru (Perú)",
42932         "pe",
42933         "51"
42934       ],
42935       [
42936         "Philippines",
42937         "ph",
42938         "63"
42939       ],
42940       [
42941         "Poland (Polska)",
42942         "pl",
42943         "48"
42944       ],
42945       [
42946         "Portugal",
42947         "pt",
42948         "351"
42949       ],
42950       [
42951         "Puerto Rico",
42952         "pr",
42953         "1",
42954         3,
42955         ["787", "939"]
42956       ],
42957       [
42958         "Qatar (‫قطر‬‎)",
42959         "qa",
42960         "974"
42961       ],
42962       [
42963         "Réunion (La Réunion)",
42964         "re",
42965         "262",
42966         0
42967       ],
42968       [
42969         "Romania (România)",
42970         "ro",
42971         "40"
42972       ],
42973       [
42974         "Russia (Россия)",
42975         "ru",
42976         "7",
42977         0
42978       ],
42979       [
42980         "Rwanda",
42981         "rw",
42982         "250"
42983       ],
42984       [
42985         "Saint Barthélemy",
42986         "bl",
42987         "590",
42988         1
42989       ],
42990       [
42991         "Saint Helena",
42992         "sh",
42993         "290"
42994       ],
42995       [
42996         "Saint Kitts and Nevis",
42997         "kn",
42998         "1869"
42999       ],
43000       [
43001         "Saint Lucia",
43002         "lc",
43003         "1758"
43004       ],
43005       [
43006         "Saint Martin (Saint-Martin (partie française))",
43007         "mf",
43008         "590",
43009         2
43010       ],
43011       [
43012         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43013         "pm",
43014         "508"
43015       ],
43016       [
43017         "Saint Vincent and the Grenadines",
43018         "vc",
43019         "1784"
43020       ],
43021       [
43022         "Samoa",
43023         "ws",
43024         "685"
43025       ],
43026       [
43027         "San Marino",
43028         "sm",
43029         "378"
43030       ],
43031       [
43032         "São Tomé and Príncipe (São Tomé e Príncipe)",
43033         "st",
43034         "239"
43035       ],
43036       [
43037         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43038         "sa",
43039         "966"
43040       ],
43041       [
43042         "Senegal (Sénégal)",
43043         "sn",
43044         "221"
43045       ],
43046       [
43047         "Serbia (Србија)",
43048         "rs",
43049         "381"
43050       ],
43051       [
43052         "Seychelles",
43053         "sc",
43054         "248"
43055       ],
43056       [
43057         "Sierra Leone",
43058         "sl",
43059         "232"
43060       ],
43061       [
43062         "Singapore",
43063         "sg",
43064         "65"
43065       ],
43066       [
43067         "Sint Maarten",
43068         "sx",
43069         "1721"
43070       ],
43071       [
43072         "Slovakia (Slovensko)",
43073         "sk",
43074         "421"
43075       ],
43076       [
43077         "Slovenia (Slovenija)",
43078         "si",
43079         "386"
43080       ],
43081       [
43082         "Solomon Islands",
43083         "sb",
43084         "677"
43085       ],
43086       [
43087         "Somalia (Soomaaliya)",
43088         "so",
43089         "252"
43090       ],
43091       [
43092         "South Africa",
43093         "za",
43094         "27"
43095       ],
43096       [
43097         "South Korea (대한민국)",
43098         "kr",
43099         "82"
43100       ],
43101       [
43102         "South Sudan (‫جنوب السودان‬‎)",
43103         "ss",
43104         "211"
43105       ],
43106       [
43107         "Spain (España)",
43108         "es",
43109         "34"
43110       ],
43111       [
43112         "Sri Lanka (ශ්‍රී ලංකාව)",
43113         "lk",
43114         "94"
43115       ],
43116       [
43117         "Sudan (‫السودان‬‎)",
43118         "sd",
43119         "249"
43120       ],
43121       [
43122         "Suriname",
43123         "sr",
43124         "597"
43125       ],
43126       [
43127         "Svalbard and Jan Mayen",
43128         "sj",
43129         "47",
43130         1
43131       ],
43132       [
43133         "Swaziland",
43134         "sz",
43135         "268"
43136       ],
43137       [
43138         "Sweden (Sverige)",
43139         "se",
43140         "46"
43141       ],
43142       [
43143         "Switzerland (Schweiz)",
43144         "ch",
43145         "41"
43146       ],
43147       [
43148         "Syria (‫سوريا‬‎)",
43149         "sy",
43150         "963"
43151       ],
43152       [
43153         "Taiwan (台灣)",
43154         "tw",
43155         "886"
43156       ],
43157       [
43158         "Tajikistan",
43159         "tj",
43160         "992"
43161       ],
43162       [
43163         "Tanzania",
43164         "tz",
43165         "255"
43166       ],
43167       [
43168         "Thailand (ไทย)",
43169         "th",
43170         "66"
43171       ],
43172       [
43173         "Timor-Leste",
43174         "tl",
43175         "670"
43176       ],
43177       [
43178         "Togo",
43179         "tg",
43180         "228"
43181       ],
43182       [
43183         "Tokelau",
43184         "tk",
43185         "690"
43186       ],
43187       [
43188         "Tonga",
43189         "to",
43190         "676"
43191       ],
43192       [
43193         "Trinidad and Tobago",
43194         "tt",
43195         "1868"
43196       ],
43197       [
43198         "Tunisia (‫تونس‬‎)",
43199         "tn",
43200         "216"
43201       ],
43202       [
43203         "Turkey (Türkiye)",
43204         "tr",
43205         "90"
43206       ],
43207       [
43208         "Turkmenistan",
43209         "tm",
43210         "993"
43211       ],
43212       [
43213         "Turks and Caicos Islands",
43214         "tc",
43215         "1649"
43216       ],
43217       [
43218         "Tuvalu",
43219         "tv",
43220         "688"
43221       ],
43222       [
43223         "U.S. Virgin Islands",
43224         "vi",
43225         "1340"
43226       ],
43227       [
43228         "Uganda",
43229         "ug",
43230         "256"
43231       ],
43232       [
43233         "Ukraine (Україна)",
43234         "ua",
43235         "380"
43236       ],
43237       [
43238         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43239         "ae",
43240         "971"
43241       ],
43242       [
43243         "United Kingdom",
43244         "gb",
43245         "44",
43246         0
43247       ],
43248       [
43249         "United States",
43250         "us",
43251         "1",
43252         0
43253       ],
43254       [
43255         "Uruguay",
43256         "uy",
43257         "598"
43258       ],
43259       [
43260         "Uzbekistan (Oʻzbekiston)",
43261         "uz",
43262         "998"
43263       ],
43264       [
43265         "Vanuatu",
43266         "vu",
43267         "678"
43268       ],
43269       [
43270         "Vatican City (Città del Vaticano)",
43271         "va",
43272         "39",
43273         1
43274       ],
43275       [
43276         "Venezuela",
43277         "ve",
43278         "58"
43279       ],
43280       [
43281         "Vietnam (Việt Nam)",
43282         "vn",
43283         "84"
43284       ],
43285       [
43286         "Wallis and Futuna (Wallis-et-Futuna)",
43287         "wf",
43288         "681"
43289       ],
43290       [
43291         "Western Sahara (‫الصحراء الغربية‬‎)",
43292         "eh",
43293         "212",
43294         1
43295       ],
43296       [
43297         "Yemen (‫اليمن‬‎)",
43298         "ye",
43299         "967"
43300       ],
43301       [
43302         "Zambia",
43303         "zm",
43304         "260"
43305       ],
43306       [
43307         "Zimbabwe",
43308         "zw",
43309         "263"
43310       ],
43311       [
43312         "Åland Islands",
43313         "ax",
43314         "358",
43315         1
43316       ]
43317   ];
43318   
43319   return d;
43320 }/**
43321 *    This script refer to:
43322 *    Title: International Telephone Input
43323 *    Author: Jack O'Connor
43324 *    Code version:  v12.1.12
43325 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43326 **/
43327
43328 /**
43329  * @class Roo.bootstrap.form.PhoneInput
43330  * @extends Roo.bootstrap.form.TriggerField
43331  * An input with International dial-code selection
43332  
43333  * @cfg {String} defaultDialCode default '+852'
43334  * @cfg {Array} preferedCountries default []
43335   
43336  * @constructor
43337  * Create a new PhoneInput.
43338  * @param {Object} config Configuration options
43339  */
43340
43341 Roo.bootstrap.form.PhoneInput = function(config) {
43342     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43343 };
43344
43345 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43346         /**
43347         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43348         */
43349         listWidth: undefined,
43350         
43351         selectedClass: 'active',
43352         
43353         invalidClass : "has-warning",
43354         
43355         validClass: 'has-success',
43356         
43357         allowed: '0123456789',
43358         
43359         max_length: 15,
43360         
43361         /**
43362          * @cfg {String} defaultDialCode The default dial code when initializing the input
43363          */
43364         defaultDialCode: '+852',
43365         
43366         /**
43367          * @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
43368          */
43369         preferedCountries: false,
43370         
43371         getAutoCreate : function()
43372         {
43373             var data = Roo.bootstrap.form.PhoneInputData();
43374             var align = this.labelAlign || this.parentLabelAlign();
43375             var id = Roo.id();
43376             
43377             this.allCountries = [];
43378             this.dialCodeMapping = [];
43379             
43380             for (var i = 0; i < data.length; i++) {
43381               var c = data[i];
43382               this.allCountries[i] = {
43383                 name: c[0],
43384                 iso2: c[1],
43385                 dialCode: c[2],
43386                 priority: c[3] || 0,
43387                 areaCodes: c[4] || null
43388               };
43389               this.dialCodeMapping[c[2]] = {
43390                   name: c[0],
43391                   iso2: c[1],
43392                   priority: c[3] || 0,
43393                   areaCodes: c[4] || null
43394               };
43395             }
43396             
43397             var cfg = {
43398                 cls: 'form-group',
43399                 cn: []
43400             };
43401             
43402             var input =  {
43403                 tag: 'input',
43404                 id : id,
43405                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43406                 maxlength: this.max_length,
43407                 cls : 'form-control tel-input',
43408                 autocomplete: 'new-password'
43409             };
43410             
43411             var hiddenInput = {
43412                 tag: 'input',
43413                 type: 'hidden',
43414                 cls: 'hidden-tel-input'
43415             };
43416             
43417             if (this.name) {
43418                 hiddenInput.name = this.name;
43419             }
43420             
43421             if (this.disabled) {
43422                 input.disabled = true;
43423             }
43424             
43425             var flag_container = {
43426                 tag: 'div',
43427                 cls: 'flag-box',
43428                 cn: [
43429                     {
43430                         tag: 'div',
43431                         cls: 'flag'
43432                     },
43433                     {
43434                         tag: 'div',
43435                         cls: 'caret'
43436                     }
43437                 ]
43438             };
43439             
43440             var box = {
43441                 tag: 'div',
43442                 cls: this.hasFeedback ? 'has-feedback' : '',
43443                 cn: [
43444                     hiddenInput,
43445                     input,
43446                     {
43447                         tag: 'input',
43448                         cls: 'dial-code-holder',
43449                         disabled: true
43450                     }
43451                 ]
43452             };
43453             
43454             var container = {
43455                 cls: 'roo-select2-container input-group',
43456                 cn: [
43457                     flag_container,
43458                     box
43459                 ]
43460             };
43461             
43462             if (this.fieldLabel.length) {
43463                 var indicator = {
43464                     tag: 'i',
43465                     tooltip: 'This field is required'
43466                 };
43467                 
43468                 var label = {
43469                     tag: 'label',
43470                     'for':  id,
43471                     cls: 'control-label',
43472                     cn: []
43473                 };
43474                 
43475                 var label_text = {
43476                     tag: 'span',
43477                     html: this.fieldLabel
43478                 };
43479                 
43480                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43481                 label.cn = [
43482                     indicator,
43483                     label_text
43484                 ];
43485                 
43486                 if(this.indicatorpos == 'right') {
43487                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43488                     label.cn = [
43489                         label_text,
43490                         indicator
43491                     ];
43492                 }
43493                 
43494                 if(align == 'left') {
43495                     container = {
43496                         tag: 'div',
43497                         cn: [
43498                             container
43499                         ]
43500                     };
43501                     
43502                     if(this.labelWidth > 12){
43503                         label.style = "width: " + this.labelWidth + 'px';
43504                     }
43505                     if(this.labelWidth < 13 && this.labelmd == 0){
43506                         this.labelmd = this.labelWidth;
43507                     }
43508                     if(this.labellg > 0){
43509                         label.cls += ' col-lg-' + this.labellg;
43510                         input.cls += ' col-lg-' + (12 - this.labellg);
43511                     }
43512                     if(this.labelmd > 0){
43513                         label.cls += ' col-md-' + this.labelmd;
43514                         container.cls += ' col-md-' + (12 - this.labelmd);
43515                     }
43516                     if(this.labelsm > 0){
43517                         label.cls += ' col-sm-' + this.labelsm;
43518                         container.cls += ' col-sm-' + (12 - this.labelsm);
43519                     }
43520                     if(this.labelxs > 0){
43521                         label.cls += ' col-xs-' + this.labelxs;
43522                         container.cls += ' col-xs-' + (12 - this.labelxs);
43523                     }
43524                 }
43525             }
43526             
43527             cfg.cn = [
43528                 label,
43529                 container
43530             ];
43531             
43532             var settings = this;
43533             
43534             ['xs','sm','md','lg'].map(function(size){
43535                 if (settings[size]) {
43536                     cfg.cls += ' col-' + size + '-' + settings[size];
43537                 }
43538             });
43539             
43540             this.store = new Roo.data.Store({
43541                 proxy : new Roo.data.MemoryProxy({}),
43542                 reader : new Roo.data.JsonReader({
43543                     fields : [
43544                         {
43545                             'name' : 'name',
43546                             'type' : 'string'
43547                         },
43548                         {
43549                             'name' : 'iso2',
43550                             'type' : 'string'
43551                         },
43552                         {
43553                             'name' : 'dialCode',
43554                             'type' : 'string'
43555                         },
43556                         {
43557                             'name' : 'priority',
43558                             'type' : 'string'
43559                         },
43560                         {
43561                             'name' : 'areaCodes',
43562                             'type' : 'string'
43563                         }
43564                     ]
43565                 })
43566             });
43567             
43568             if(!this.preferedCountries) {
43569                 this.preferedCountries = [
43570                     'hk',
43571                     'gb',
43572                     'us'
43573                 ];
43574             }
43575             
43576             var p = this.preferedCountries.reverse();
43577             
43578             if(p) {
43579                 for (var i = 0; i < p.length; i++) {
43580                     for (var j = 0; j < this.allCountries.length; j++) {
43581                         if(this.allCountries[j].iso2 == p[i]) {
43582                             var t = this.allCountries[j];
43583                             this.allCountries.splice(j,1);
43584                             this.allCountries.unshift(t);
43585                         }
43586                     } 
43587                 }
43588             }
43589             
43590             this.store.proxy.data = {
43591                 success: true,
43592                 data: this.allCountries
43593             };
43594             
43595             return cfg;
43596         },
43597         
43598         initEvents : function()
43599         {
43600             this.createList();
43601             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43602             
43603             this.indicator = this.indicatorEl();
43604             this.flag = this.flagEl();
43605             this.dialCodeHolder = this.dialCodeHolderEl();
43606             
43607             this.trigger = this.el.select('div.flag-box',true).first();
43608             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43609             
43610             var _this = this;
43611             
43612             (function(){
43613                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43614                 _this.list.setWidth(lw);
43615             }).defer(100);
43616             
43617             this.list.on('mouseover', this.onViewOver, this);
43618             this.list.on('mousemove', this.onViewMove, this);
43619             this.inputEl().on("keyup", this.onKeyUp, this);
43620             this.inputEl().on("keypress", this.onKeyPress, this);
43621             
43622             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43623
43624             this.view = new Roo.View(this.list, this.tpl, {
43625                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43626             });
43627             
43628             this.view.on('click', this.onViewClick, this);
43629             this.setValue(this.defaultDialCode);
43630         },
43631         
43632         onTriggerClick : function(e)
43633         {
43634             Roo.log('trigger click');
43635             if(this.disabled){
43636                 return;
43637             }
43638             
43639             if(this.isExpanded()){
43640                 this.collapse();
43641                 this.hasFocus = false;
43642             }else {
43643                 this.store.load({});
43644                 this.hasFocus = true;
43645                 this.expand();
43646             }
43647         },
43648         
43649         isExpanded : function()
43650         {
43651             return this.list.isVisible();
43652         },
43653         
43654         collapse : function()
43655         {
43656             if(!this.isExpanded()){
43657                 return;
43658             }
43659             this.list.hide();
43660             Roo.get(document).un('mousedown', this.collapseIf, this);
43661             Roo.get(document).un('mousewheel', this.collapseIf, this);
43662             this.fireEvent('collapse', this);
43663             this.validate();
43664         },
43665         
43666         expand : function()
43667         {
43668             Roo.log('expand');
43669
43670             if(this.isExpanded() || !this.hasFocus){
43671                 return;
43672             }
43673             
43674             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43675             this.list.setWidth(lw);
43676             
43677             this.list.show();
43678             this.restrictHeight();
43679             
43680             Roo.get(document).on('mousedown', this.collapseIf, this);
43681             Roo.get(document).on('mousewheel', this.collapseIf, this);
43682             
43683             this.fireEvent('expand', this);
43684         },
43685         
43686         restrictHeight : function()
43687         {
43688             this.list.alignTo(this.inputEl(), this.listAlign);
43689             this.list.alignTo(this.inputEl(), this.listAlign);
43690         },
43691         
43692         onViewOver : function(e, t)
43693         {
43694             if(this.inKeyMode){
43695                 return;
43696             }
43697             var item = this.view.findItemFromChild(t);
43698             
43699             if(item){
43700                 var index = this.view.indexOf(item);
43701                 this.select(index, false);
43702             }
43703         },
43704
43705         // private
43706         onViewClick : function(view, doFocus, el, e)
43707         {
43708             var index = this.view.getSelectedIndexes()[0];
43709             
43710             var r = this.store.getAt(index);
43711             
43712             if(r){
43713                 this.onSelect(r, index);
43714             }
43715             if(doFocus !== false && !this.blockFocus){
43716                 this.inputEl().focus();
43717             }
43718         },
43719         
43720         onViewMove : function(e, t)
43721         {
43722             this.inKeyMode = false;
43723         },
43724         
43725         select : function(index, scrollIntoView)
43726         {
43727             this.selectedIndex = index;
43728             this.view.select(index);
43729             if(scrollIntoView !== false){
43730                 var el = this.view.getNode(index);
43731                 if(el){
43732                     this.list.scrollChildIntoView(el, false);
43733                 }
43734             }
43735         },
43736         
43737         createList : function()
43738         {
43739             this.list = Roo.get(document.body).createChild({
43740                 tag: 'ul',
43741                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43742                 style: 'display:none'
43743             });
43744             
43745             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43746         },
43747         
43748         collapseIf : function(e)
43749         {
43750             var in_combo  = e.within(this.el);
43751             var in_list =  e.within(this.list);
43752             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43753             
43754             if (in_combo || in_list || is_list) {
43755                 return;
43756             }
43757             this.collapse();
43758         },
43759         
43760         onSelect : function(record, index)
43761         {
43762             if(this.fireEvent('beforeselect', this, record, index) !== false){
43763                 
43764                 this.setFlagClass(record.data.iso2);
43765                 this.setDialCode(record.data.dialCode);
43766                 this.hasFocus = false;
43767                 this.collapse();
43768                 this.fireEvent('select', this, record, index);
43769             }
43770         },
43771         
43772         flagEl : function()
43773         {
43774             var flag = this.el.select('div.flag',true).first();
43775             if(!flag){
43776                 return false;
43777             }
43778             return flag;
43779         },
43780         
43781         dialCodeHolderEl : function()
43782         {
43783             var d = this.el.select('input.dial-code-holder',true).first();
43784             if(!d){
43785                 return false;
43786             }
43787             return d;
43788         },
43789         
43790         setDialCode : function(v)
43791         {
43792             this.dialCodeHolder.dom.value = '+'+v;
43793         },
43794         
43795         setFlagClass : function(n)
43796         {
43797             this.flag.dom.className = 'flag '+n;
43798         },
43799         
43800         getValue : function()
43801         {
43802             var v = this.inputEl().getValue();
43803             if(this.dialCodeHolder) {
43804                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43805             }
43806             return v;
43807         },
43808         
43809         setValue : function(v)
43810         {
43811             var d = this.getDialCode(v);
43812             
43813             //invalid dial code
43814             if(v.length == 0 || !d || d.length == 0) {
43815                 if(this.rendered){
43816                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43817                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43818                 }
43819                 return;
43820             }
43821             
43822             //valid dial code
43823             this.setFlagClass(this.dialCodeMapping[d].iso2);
43824             this.setDialCode(d);
43825             this.inputEl().dom.value = v.replace('+'+d,'');
43826             this.hiddenEl().dom.value = this.getValue();
43827             
43828             this.validate();
43829         },
43830         
43831         getDialCode : function(v)
43832         {
43833             v = v ||  '';
43834             
43835             if (v.length == 0) {
43836                 return this.dialCodeHolder.dom.value;
43837             }
43838             
43839             var dialCode = "";
43840             if (v.charAt(0) != "+") {
43841                 return false;
43842             }
43843             var numericChars = "";
43844             for (var i = 1; i < v.length; i++) {
43845               var c = v.charAt(i);
43846               if (!isNaN(c)) {
43847                 numericChars += c;
43848                 if (this.dialCodeMapping[numericChars]) {
43849                   dialCode = v.substr(1, i);
43850                 }
43851                 if (numericChars.length == 4) {
43852                   break;
43853                 }
43854               }
43855             }
43856             return dialCode;
43857         },
43858         
43859         reset : function()
43860         {
43861             this.setValue(this.defaultDialCode);
43862             this.validate();
43863         },
43864         
43865         hiddenEl : function()
43866         {
43867             return this.el.select('input.hidden-tel-input',true).first();
43868         },
43869         
43870         // after setting val
43871         onKeyUp : function(e){
43872             this.setValue(this.getValue());
43873         },
43874         
43875         onKeyPress : function(e){
43876             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43877                 e.stopEvent();
43878             }
43879         }
43880         
43881 });
43882 /**
43883  * @class Roo.bootstrap.form.MoneyField
43884  * @extends Roo.bootstrap.form.ComboBox
43885  * Bootstrap MoneyField class
43886  * 
43887  * @constructor
43888  * Create a new MoneyField.
43889  * @param {Object} config Configuration options
43890  */
43891
43892 Roo.bootstrap.form.MoneyField = function(config) {
43893     
43894     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43895     
43896 };
43897
43898 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43899     
43900     /**
43901      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43902      */
43903     allowDecimals : true,
43904     /**
43905      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43906      */
43907     decimalSeparator : ".",
43908     /**
43909      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43910      */
43911     decimalPrecision : 0,
43912     /**
43913      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43914      */
43915     allowNegative : true,
43916     /**
43917      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43918      */
43919     allowZero: true,
43920     /**
43921      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43922      */
43923     minValue : Number.NEGATIVE_INFINITY,
43924     /**
43925      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43926      */
43927     maxValue : Number.MAX_VALUE,
43928     /**
43929      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43930      */
43931     minText : "The minimum value for this field is {0}",
43932     /**
43933      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43934      */
43935     maxText : "The maximum value for this field is {0}",
43936     /**
43937      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43938      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43939      */
43940     nanText : "{0} is not a valid number",
43941     /**
43942      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43943      */
43944     castInt : true,
43945     /**
43946      * @cfg {String} defaults currency of the MoneyField
43947      * value should be in lkey
43948      */
43949     defaultCurrency : false,
43950     /**
43951      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43952      */
43953     thousandsDelimiter : false,
43954     /**
43955      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43956      */
43957     max_length: false,
43958     
43959     inputlg : 9,
43960     inputmd : 9,
43961     inputsm : 9,
43962     inputxs : 6,
43963      /**
43964      * @cfg {Roo.data.Store} store  Store to lookup currency??
43965      */
43966     store : false,
43967     
43968     getAutoCreate : function()
43969     {
43970         var align = this.labelAlign || this.parentLabelAlign();
43971         
43972         var id = Roo.id();
43973
43974         var cfg = {
43975             cls: 'form-group',
43976             cn: []
43977         };
43978
43979         var input =  {
43980             tag: 'input',
43981             id : id,
43982             cls : 'form-control roo-money-amount-input',
43983             autocomplete: 'new-password'
43984         };
43985         
43986         var hiddenInput = {
43987             tag: 'input',
43988             type: 'hidden',
43989             id: Roo.id(),
43990             cls: 'hidden-number-input'
43991         };
43992         
43993         if(this.max_length) {
43994             input.maxlength = this.max_length; 
43995         }
43996         
43997         if (this.name) {
43998             hiddenInput.name = this.name;
43999         }
44000
44001         if (this.disabled) {
44002             input.disabled = true;
44003         }
44004
44005         var clg = 12 - this.inputlg;
44006         var cmd = 12 - this.inputmd;
44007         var csm = 12 - this.inputsm;
44008         var cxs = 12 - this.inputxs;
44009         
44010         var container = {
44011             tag : 'div',
44012             cls : 'row roo-money-field',
44013             cn : [
44014                 {
44015                     tag : 'div',
44016                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44017                     cn : [
44018                         {
44019                             tag : 'div',
44020                             cls: 'roo-select2-container input-group',
44021                             cn: [
44022                                 {
44023                                     tag : 'input',
44024                                     cls : 'form-control roo-money-currency-input',
44025                                     autocomplete: 'new-password',
44026                                     readOnly : 1,
44027                                     name : this.currencyName
44028                                 },
44029                                 {
44030                                     tag :'span',
44031                                     cls : 'input-group-addon',
44032                                     cn : [
44033                                         {
44034                                             tag: 'span',
44035                                             cls: 'caret'
44036                                         }
44037                                     ]
44038                                 }
44039                             ]
44040                         }
44041                     ]
44042                 },
44043                 {
44044                     tag : 'div',
44045                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44046                     cn : [
44047                         {
44048                             tag: 'div',
44049                             cls: this.hasFeedback ? 'has-feedback' : '',
44050                             cn: [
44051                                 input
44052                             ]
44053                         }
44054                     ]
44055                 }
44056             ]
44057             
44058         };
44059         
44060         if (this.fieldLabel.length) {
44061             var indicator = {
44062                 tag: 'i',
44063                 tooltip: 'This field is required'
44064             };
44065
44066             var label = {
44067                 tag: 'label',
44068                 'for':  id,
44069                 cls: 'control-label',
44070                 cn: []
44071             };
44072
44073             var label_text = {
44074                 tag: 'span',
44075                 html: this.fieldLabel
44076             };
44077
44078             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44079             label.cn = [
44080                 indicator,
44081                 label_text
44082             ];
44083
44084             if(this.indicatorpos == 'right') {
44085                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44086                 label.cn = [
44087                     label_text,
44088                     indicator
44089                 ];
44090             }
44091
44092             if(align == 'left') {
44093                 container = {
44094                     tag: 'div',
44095                     cn: [
44096                         container
44097                     ]
44098                 };
44099
44100                 if(this.labelWidth > 12){
44101                     label.style = "width: " + this.labelWidth + 'px';
44102                 }
44103                 if(this.labelWidth < 13 && this.labelmd == 0){
44104                     this.labelmd = this.labelWidth;
44105                 }
44106                 if(this.labellg > 0){
44107                     label.cls += ' col-lg-' + this.labellg;
44108                     input.cls += ' col-lg-' + (12 - this.labellg);
44109                 }
44110                 if(this.labelmd > 0){
44111                     label.cls += ' col-md-' + this.labelmd;
44112                     container.cls += ' col-md-' + (12 - this.labelmd);
44113                 }
44114                 if(this.labelsm > 0){
44115                     label.cls += ' col-sm-' + this.labelsm;
44116                     container.cls += ' col-sm-' + (12 - this.labelsm);
44117                 }
44118                 if(this.labelxs > 0){
44119                     label.cls += ' col-xs-' + this.labelxs;
44120                     container.cls += ' col-xs-' + (12 - this.labelxs);
44121                 }
44122             }
44123         }
44124
44125         cfg.cn = [
44126             label,
44127             container,
44128             hiddenInput
44129         ];
44130         
44131         var settings = this;
44132
44133         ['xs','sm','md','lg'].map(function(size){
44134             if (settings[size]) {
44135                 cfg.cls += ' col-' + size + '-' + settings[size];
44136             }
44137         });
44138         
44139         return cfg;
44140     },
44141     
44142     initEvents : function()
44143     {
44144         this.indicator = this.indicatorEl();
44145         
44146         this.initCurrencyEvent();
44147         
44148         this.initNumberEvent();
44149     },
44150     
44151     initCurrencyEvent : function()
44152     {
44153         if (!this.store) {
44154             throw "can not find store for combo";
44155         }
44156         
44157         this.store = Roo.factory(this.store, Roo.data);
44158         this.store.parent = this;
44159         
44160         this.createList();
44161         
44162         this.triggerEl = this.el.select('.input-group-addon', true).first();
44163         
44164         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44165         
44166         var _this = this;
44167         
44168         (function(){
44169             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44170             _this.list.setWidth(lw);
44171         }).defer(100);
44172         
44173         this.list.on('mouseover', this.onViewOver, this);
44174         this.list.on('mousemove', this.onViewMove, this);
44175         this.list.on('scroll', this.onViewScroll, this);
44176         
44177         if(!this.tpl){
44178             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44179         }
44180         
44181         this.view = new Roo.View(this.list, this.tpl, {
44182             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44183         });
44184         
44185         this.view.on('click', this.onViewClick, this);
44186         
44187         this.store.on('beforeload', this.onBeforeLoad, this);
44188         this.store.on('load', this.onLoad, this);
44189         this.store.on('loadexception', this.onLoadException, this);
44190         
44191         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44192             "up" : function(e){
44193                 this.inKeyMode = true;
44194                 this.selectPrev();
44195             },
44196
44197             "down" : function(e){
44198                 if(!this.isExpanded()){
44199                     this.onTriggerClick();
44200                 }else{
44201                     this.inKeyMode = true;
44202                     this.selectNext();
44203                 }
44204             },
44205
44206             "enter" : function(e){
44207                 this.collapse();
44208                 
44209                 if(this.fireEvent("specialkey", this, e)){
44210                     this.onViewClick(false);
44211                 }
44212                 
44213                 return true;
44214             },
44215
44216             "esc" : function(e){
44217                 this.collapse();
44218             },
44219
44220             "tab" : function(e){
44221                 this.collapse();
44222                 
44223                 if(this.fireEvent("specialkey", this, e)){
44224                     this.onViewClick(false);
44225                 }
44226                 
44227                 return true;
44228             },
44229
44230             scope : this,
44231
44232             doRelay : function(foo, bar, hname){
44233                 if(hname == 'down' || this.scope.isExpanded()){
44234                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44235                 }
44236                 return true;
44237             },
44238
44239             forceKeyDown: true
44240         });
44241         
44242         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44243         
44244     },
44245     
44246     initNumberEvent : function(e)
44247     {
44248         this.inputEl().on("keydown" , this.fireKey,  this);
44249         this.inputEl().on("focus", this.onFocus,  this);
44250         this.inputEl().on("blur", this.onBlur,  this);
44251         
44252         this.inputEl().relayEvent('keyup', this);
44253         
44254         if(this.indicator){
44255             this.indicator.addClass('invisible');
44256         }
44257  
44258         this.originalValue = this.getValue();
44259         
44260         if(this.validationEvent == 'keyup'){
44261             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44262             this.inputEl().on('keyup', this.filterValidation, this);
44263         }
44264         else if(this.validationEvent !== false){
44265             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44266         }
44267         
44268         if(this.selectOnFocus){
44269             this.on("focus", this.preFocus, this);
44270             
44271         }
44272         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44273             this.inputEl().on("keypress", this.filterKeys, this);
44274         } else {
44275             this.inputEl().relayEvent('keypress', this);
44276         }
44277         
44278         var allowed = "0123456789";
44279         
44280         if(this.allowDecimals){
44281             allowed += this.decimalSeparator;
44282         }
44283         
44284         if(this.allowNegative){
44285             allowed += "-";
44286         }
44287         
44288         if(this.thousandsDelimiter) {
44289             allowed += ",";
44290         }
44291         
44292         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44293         
44294         var keyPress = function(e){
44295             
44296             var k = e.getKey();
44297             
44298             var c = e.getCharCode();
44299             
44300             if(
44301                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44302                     allowed.indexOf(String.fromCharCode(c)) === -1
44303             ){
44304                 e.stopEvent();
44305                 return;
44306             }
44307             
44308             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44309                 return;
44310             }
44311             
44312             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44313                 e.stopEvent();
44314             }
44315         };
44316         
44317         this.inputEl().on("keypress", keyPress, this);
44318         
44319     },
44320     
44321     onTriggerClick : function(e)
44322     {   
44323         if(this.disabled){
44324             return;
44325         }
44326         
44327         this.page = 0;
44328         this.loadNext = false;
44329         
44330         if(this.isExpanded()){
44331             this.collapse();
44332             return;
44333         }
44334         
44335         this.hasFocus = true;
44336         
44337         if(this.triggerAction == 'all') {
44338             this.doQuery(this.allQuery, true);
44339             return;
44340         }
44341         
44342         this.doQuery(this.getRawValue());
44343     },
44344     
44345     getCurrency : function()
44346     {   
44347         var v = this.currencyEl().getValue();
44348         
44349         return v;
44350     },
44351     
44352     restrictHeight : function()
44353     {
44354         this.list.alignTo(this.currencyEl(), this.listAlign);
44355         this.list.alignTo(this.currencyEl(), this.listAlign);
44356     },
44357     
44358     onViewClick : function(view, doFocus, el, e)
44359     {
44360         var index = this.view.getSelectedIndexes()[0];
44361         
44362         var r = this.store.getAt(index);
44363         
44364         if(r){
44365             this.onSelect(r, index);
44366         }
44367     },
44368     
44369     onSelect : function(record, index){
44370         
44371         if(this.fireEvent('beforeselect', this, record, index) !== false){
44372         
44373             this.setFromCurrencyData(index > -1 ? record.data : false);
44374             
44375             this.collapse();
44376             
44377             this.fireEvent('select', this, record, index);
44378         }
44379     },
44380     
44381     setFromCurrencyData : function(o)
44382     {
44383         var currency = '';
44384         
44385         this.lastCurrency = o;
44386         
44387         if (this.currencyField) {
44388             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44389         } else {
44390             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44391         }
44392         
44393         this.lastSelectionText = currency;
44394         
44395         //setting default currency
44396         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44397             this.setCurrency(this.defaultCurrency);
44398             return;
44399         }
44400         
44401         this.setCurrency(currency);
44402     },
44403     
44404     setFromData : function(o)
44405     {
44406         var c = {};
44407         
44408         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44409         
44410         this.setFromCurrencyData(c);
44411         
44412         var value = '';
44413         
44414         if (this.name) {
44415             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44416         } else {
44417             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44418         }
44419         
44420         this.setValue(value);
44421         
44422     },
44423     
44424     setCurrency : function(v)
44425     {   
44426         this.currencyValue = v;
44427         
44428         if(this.rendered){
44429             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44430             this.validate();
44431         }
44432     },
44433     
44434     setValue : function(v)
44435     {
44436         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44437         
44438         this.value = v;
44439         
44440         if(this.rendered){
44441             
44442             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44443             
44444             this.inputEl().dom.value = (v == '') ? '' :
44445                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44446             
44447             if(!this.allowZero && v === '0') {
44448                 this.hiddenEl().dom.value = '';
44449                 this.inputEl().dom.value = '';
44450             }
44451             
44452             this.validate();
44453         }
44454     },
44455     
44456     getRawValue : function()
44457     {
44458         var v = this.inputEl().getValue();
44459         
44460         return v;
44461     },
44462     
44463     getValue : function()
44464     {
44465         return this.fixPrecision(this.parseValue(this.getRawValue()));
44466     },
44467     
44468     parseValue : function(value)
44469     {
44470         if(this.thousandsDelimiter) {
44471             value += "";
44472             r = new RegExp(",", "g");
44473             value = value.replace(r, "");
44474         }
44475         
44476         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44477         return isNaN(value) ? '' : value;
44478         
44479     },
44480     
44481     fixPrecision : function(value)
44482     {
44483         if(this.thousandsDelimiter) {
44484             value += "";
44485             r = new RegExp(",", "g");
44486             value = value.replace(r, "");
44487         }
44488         
44489         var nan = isNaN(value);
44490         
44491         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44492             return nan ? '' : value;
44493         }
44494         return parseFloat(value).toFixed(this.decimalPrecision);
44495     },
44496     
44497     decimalPrecisionFcn : function(v)
44498     {
44499         return Math.floor(v);
44500     },
44501     
44502     validateValue : function(value)
44503     {
44504         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44505             return false;
44506         }
44507         
44508         var num = this.parseValue(value);
44509         
44510         if(isNaN(num)){
44511             this.markInvalid(String.format(this.nanText, value));
44512             return false;
44513         }
44514         
44515         if(num < this.minValue){
44516             this.markInvalid(String.format(this.minText, this.minValue));
44517             return false;
44518         }
44519         
44520         if(num > this.maxValue){
44521             this.markInvalid(String.format(this.maxText, this.maxValue));
44522             return false;
44523         }
44524         
44525         return true;
44526     },
44527     
44528     validate : function()
44529     {
44530         if(this.disabled || this.allowBlank){
44531             this.markValid();
44532             return true;
44533         }
44534         
44535         var currency = this.getCurrency();
44536         
44537         if(this.validateValue(this.getRawValue()) && currency.length){
44538             this.markValid();
44539             return true;
44540         }
44541         
44542         this.markInvalid();
44543         return false;
44544     },
44545     
44546     getName: function()
44547     {
44548         return this.name;
44549     },
44550     
44551     beforeBlur : function()
44552     {
44553         if(!this.castInt){
44554             return;
44555         }
44556         
44557         var v = this.parseValue(this.getRawValue());
44558         
44559         if(v || v == 0){
44560             this.setValue(v);
44561         }
44562     },
44563     
44564     onBlur : function()
44565     {
44566         this.beforeBlur();
44567         
44568         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44569             //this.el.removeClass(this.focusClass);
44570         }
44571         
44572         this.hasFocus = false;
44573         
44574         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44575             this.validate();
44576         }
44577         
44578         var v = this.getValue();
44579         
44580         if(String(v) !== String(this.startValue)){
44581             this.fireEvent('change', this, v, this.startValue);
44582         }
44583         
44584         this.fireEvent("blur", this);
44585     },
44586     
44587     inputEl : function()
44588     {
44589         return this.el.select('.roo-money-amount-input', true).first();
44590     },
44591     
44592     currencyEl : function()
44593     {
44594         return this.el.select('.roo-money-currency-input', true).first();
44595     },
44596     
44597     hiddenEl : function()
44598     {
44599         return this.el.select('input.hidden-number-input',true).first();
44600     }
44601     
44602 });/**
44603  * @class Roo.bootstrap.BezierSignature
44604  * @extends Roo.bootstrap.Component
44605  * Bootstrap BezierSignature class
44606  * This script refer to:
44607  *    Title: Signature Pad
44608  *    Author: szimek
44609  *    Availability: https://github.com/szimek/signature_pad
44610  *
44611  * @constructor
44612  * Create a new BezierSignature
44613  * @param {Object} config The config object
44614  */
44615
44616 Roo.bootstrap.BezierSignature = function(config){
44617     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44618     this.addEvents({
44619         "resize" : true
44620     });
44621 };
44622
44623 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44624 {
44625      
44626     curve_data: [],
44627     
44628     is_empty: true,
44629     
44630     mouse_btn_down: true,
44631     
44632     /**
44633      * @cfg {int} canvas height
44634      */
44635     canvas_height: '200px',
44636     
44637     /**
44638      * @cfg {float|function} Radius of a single dot.
44639      */ 
44640     dot_size: false,
44641     
44642     /**
44643      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44644      */
44645     min_width: 0.5,
44646     
44647     /**
44648      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44649      */
44650     max_width: 2.5,
44651     
44652     /**
44653      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44654      */
44655     throttle: 16,
44656     
44657     /**
44658      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44659      */
44660     min_distance: 5,
44661     
44662     /**
44663      * @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.
44664      */
44665     bg_color: 'rgba(0, 0, 0, 0)',
44666     
44667     /**
44668      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44669      */
44670     dot_color: 'black',
44671     
44672     /**
44673      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44674      */ 
44675     velocity_filter_weight: 0.7,
44676     
44677     /**
44678      * @cfg {function} Callback when stroke begin. 
44679      */
44680     onBegin: false,
44681     
44682     /**
44683      * @cfg {function} Callback when stroke end.
44684      */
44685     onEnd: false,
44686     
44687     getAutoCreate : function()
44688     {
44689         var cls = 'roo-signature column';
44690         
44691         if(this.cls){
44692             cls += ' ' + this.cls;
44693         }
44694         
44695         var col_sizes = [
44696             'lg',
44697             'md',
44698             'sm',
44699             'xs'
44700         ];
44701         
44702         for(var i = 0; i < col_sizes.length; i++) {
44703             if(this[col_sizes[i]]) {
44704                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44705             }
44706         }
44707         
44708         var cfg = {
44709             tag: 'div',
44710             cls: cls,
44711             cn: [
44712                 {
44713                     tag: 'div',
44714                     cls: 'roo-signature-body',
44715                     cn: [
44716                         {
44717                             tag: 'canvas',
44718                             cls: 'roo-signature-body-canvas',
44719                             height: this.canvas_height,
44720                             width: this.canvas_width
44721                         }
44722                     ]
44723                 },
44724                 {
44725                     tag: 'input',
44726                     type: 'file',
44727                     style: 'display: none'
44728                 }
44729             ]
44730         };
44731         
44732         return cfg;
44733     },
44734     
44735     initEvents: function() 
44736     {
44737         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44738         
44739         var canvas = this.canvasEl();
44740         
44741         // mouse && touch event swapping...
44742         canvas.dom.style.touchAction = 'none';
44743         canvas.dom.style.msTouchAction = 'none';
44744         
44745         this.mouse_btn_down = false;
44746         canvas.on('mousedown', this._handleMouseDown, this);
44747         canvas.on('mousemove', this._handleMouseMove, this);
44748         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44749         
44750         if (window.PointerEvent) {
44751             canvas.on('pointerdown', this._handleMouseDown, this);
44752             canvas.on('pointermove', this._handleMouseMove, this);
44753             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44754         }
44755         
44756         if ('ontouchstart' in window) {
44757             canvas.on('touchstart', this._handleTouchStart, this);
44758             canvas.on('touchmove', this._handleTouchMove, this);
44759             canvas.on('touchend', this._handleTouchEnd, this);
44760         }
44761         
44762         Roo.EventManager.onWindowResize(this.resize, this, true);
44763         
44764         // file input event
44765         this.fileEl().on('change', this.uploadImage, this);
44766         
44767         this.clear();
44768         
44769         this.resize();
44770     },
44771     
44772     resize: function(){
44773         
44774         var canvas = this.canvasEl().dom;
44775         var ctx = this.canvasElCtx();
44776         var img_data = false;
44777         
44778         if(canvas.width > 0) {
44779             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44780         }
44781         // setting canvas width will clean img data
44782         canvas.width = 0;
44783         
44784         var style = window.getComputedStyle ? 
44785             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44786             
44787         var padding_left = parseInt(style.paddingLeft) || 0;
44788         var padding_right = parseInt(style.paddingRight) || 0;
44789         
44790         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44791         
44792         if(img_data) {
44793             ctx.putImageData(img_data, 0, 0);
44794         }
44795     },
44796     
44797     _handleMouseDown: function(e)
44798     {
44799         if (e.browserEvent.which === 1) {
44800             this.mouse_btn_down = true;
44801             this.strokeBegin(e);
44802         }
44803     },
44804     
44805     _handleMouseMove: function (e)
44806     {
44807         if (this.mouse_btn_down) {
44808             this.strokeMoveUpdate(e);
44809         }
44810     },
44811     
44812     _handleMouseUp: function (e)
44813     {
44814         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44815             this.mouse_btn_down = false;
44816             this.strokeEnd(e);
44817         }
44818     },
44819     
44820     _handleTouchStart: function (e) {
44821         
44822         e.preventDefault();
44823         if (e.browserEvent.targetTouches.length === 1) {
44824             // var touch = e.browserEvent.changedTouches[0];
44825             // this.strokeBegin(touch);
44826             
44827              this.strokeBegin(e); // assume e catching the correct xy...
44828         }
44829     },
44830     
44831     _handleTouchMove: function (e) {
44832         e.preventDefault();
44833         // var touch = event.targetTouches[0];
44834         // _this._strokeMoveUpdate(touch);
44835         this.strokeMoveUpdate(e);
44836     },
44837     
44838     _handleTouchEnd: function (e) {
44839         var wasCanvasTouched = e.target === this.canvasEl().dom;
44840         if (wasCanvasTouched) {
44841             e.preventDefault();
44842             // var touch = event.changedTouches[0];
44843             // _this._strokeEnd(touch);
44844             this.strokeEnd(e);
44845         }
44846     },
44847     
44848     reset: function () {
44849         this._lastPoints = [];
44850         this._lastVelocity = 0;
44851         this._lastWidth = (this.min_width + this.max_width) / 2;
44852         this.canvasElCtx().fillStyle = this.dot_color;
44853     },
44854     
44855     strokeMoveUpdate: function(e)
44856     {
44857         this.strokeUpdate(e);
44858         
44859         if (this.throttle) {
44860             this.throttleStroke(this.strokeUpdate, this.throttle);
44861         }
44862         else {
44863             this.strokeUpdate(e);
44864         }
44865     },
44866     
44867     strokeBegin: function(e)
44868     {
44869         var newPointGroup = {
44870             color: this.dot_color,
44871             points: []
44872         };
44873         
44874         if (typeof this.onBegin === 'function') {
44875             this.onBegin(e);
44876         }
44877         
44878         this.curve_data.push(newPointGroup);
44879         this.reset();
44880         this.strokeUpdate(e);
44881     },
44882     
44883     strokeUpdate: function(e)
44884     {
44885         var rect = this.canvasEl().dom.getBoundingClientRect();
44886         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44887         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44888         var lastPoints = lastPointGroup.points;
44889         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44890         var isLastPointTooClose = lastPoint
44891             ? point.distanceTo(lastPoint) <= this.min_distance
44892             : false;
44893         var color = lastPointGroup.color;
44894         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44895             var curve = this.addPoint(point);
44896             if (!lastPoint) {
44897                 this.drawDot({color: color, point: point});
44898             }
44899             else if (curve) {
44900                 this.drawCurve({color: color, curve: curve});
44901             }
44902             lastPoints.push({
44903                 time: point.time,
44904                 x: point.x,
44905                 y: point.y
44906             });
44907         }
44908     },
44909     
44910     strokeEnd: function(e)
44911     {
44912         this.strokeUpdate(e);
44913         if (typeof this.onEnd === 'function') {
44914             this.onEnd(e);
44915         }
44916     },
44917     
44918     addPoint:  function (point) {
44919         var _lastPoints = this._lastPoints;
44920         _lastPoints.push(point);
44921         if (_lastPoints.length > 2) {
44922             if (_lastPoints.length === 3) {
44923                 _lastPoints.unshift(_lastPoints[0]);
44924             }
44925             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44926             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44927             _lastPoints.shift();
44928             return curve;
44929         }
44930         return null;
44931     },
44932     
44933     calculateCurveWidths: function (startPoint, endPoint) {
44934         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44935             (1 - this.velocity_filter_weight) * this._lastVelocity;
44936
44937         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44938         var widths = {
44939             end: newWidth,
44940             start: this._lastWidth
44941         };
44942         
44943         this._lastVelocity = velocity;
44944         this._lastWidth = newWidth;
44945         return widths;
44946     },
44947     
44948     drawDot: function (_a) {
44949         var color = _a.color, point = _a.point;
44950         var ctx = this.canvasElCtx();
44951         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44952         ctx.beginPath();
44953         this.drawCurveSegment(point.x, point.y, width);
44954         ctx.closePath();
44955         ctx.fillStyle = color;
44956         ctx.fill();
44957     },
44958     
44959     drawCurve: function (_a) {
44960         var color = _a.color, curve = _a.curve;
44961         var ctx = this.canvasElCtx();
44962         var widthDelta = curve.endWidth - curve.startWidth;
44963         var drawSteps = Math.floor(curve.length()) * 2;
44964         ctx.beginPath();
44965         ctx.fillStyle = color;
44966         for (var i = 0; i < drawSteps; i += 1) {
44967         var t = i / drawSteps;
44968         var tt = t * t;
44969         var ttt = tt * t;
44970         var u = 1 - t;
44971         var uu = u * u;
44972         var uuu = uu * u;
44973         var x = uuu * curve.startPoint.x;
44974         x += 3 * uu * t * curve.control1.x;
44975         x += 3 * u * tt * curve.control2.x;
44976         x += ttt * curve.endPoint.x;
44977         var y = uuu * curve.startPoint.y;
44978         y += 3 * uu * t * curve.control1.y;
44979         y += 3 * u * tt * curve.control2.y;
44980         y += ttt * curve.endPoint.y;
44981         var width = curve.startWidth + ttt * widthDelta;
44982         this.drawCurveSegment(x, y, width);
44983         }
44984         ctx.closePath();
44985         ctx.fill();
44986     },
44987     
44988     drawCurveSegment: function (x, y, width) {
44989         var ctx = this.canvasElCtx();
44990         ctx.moveTo(x, y);
44991         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44992         this.is_empty = false;
44993     },
44994     
44995     clear: function()
44996     {
44997         var ctx = this.canvasElCtx();
44998         var canvas = this.canvasEl().dom;
44999         ctx.fillStyle = this.bg_color;
45000         ctx.clearRect(0, 0, canvas.width, canvas.height);
45001         ctx.fillRect(0, 0, canvas.width, canvas.height);
45002         this.curve_data = [];
45003         this.reset();
45004         this.is_empty = true;
45005     },
45006     
45007     fileEl: function()
45008     {
45009         return  this.el.select('input',true).first();
45010     },
45011     
45012     canvasEl: function()
45013     {
45014         return this.el.select('canvas',true).first();
45015     },
45016     
45017     canvasElCtx: function()
45018     {
45019         return this.el.select('canvas',true).first().dom.getContext('2d');
45020     },
45021     
45022     getImage: function(type)
45023     {
45024         if(this.is_empty) {
45025             return false;
45026         }
45027         
45028         // encryption ?
45029         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45030     },
45031     
45032     drawFromImage: function(img_src)
45033     {
45034         var img = new Image();
45035         
45036         img.onload = function(){
45037             this.canvasElCtx().drawImage(img, 0, 0);
45038         }.bind(this);
45039         
45040         img.src = img_src;
45041         
45042         this.is_empty = false;
45043     },
45044     
45045     selectImage: function()
45046     {
45047         this.fileEl().dom.click();
45048     },
45049     
45050     uploadImage: function(e)
45051     {
45052         var reader = new FileReader();
45053         
45054         reader.onload = function(e){
45055             var img = new Image();
45056             img.onload = function(){
45057                 this.reset();
45058                 this.canvasElCtx().drawImage(img, 0, 0);
45059             }.bind(this);
45060             img.src = e.target.result;
45061         }.bind(this);
45062         
45063         reader.readAsDataURL(e.target.files[0]);
45064     },
45065     
45066     // Bezier Point Constructor
45067     Point: (function () {
45068         function Point(x, y, time) {
45069             this.x = x;
45070             this.y = y;
45071             this.time = time || Date.now();
45072         }
45073         Point.prototype.distanceTo = function (start) {
45074             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45075         };
45076         Point.prototype.equals = function (other) {
45077             return this.x === other.x && this.y === other.y && this.time === other.time;
45078         };
45079         Point.prototype.velocityFrom = function (start) {
45080             return this.time !== start.time
45081             ? this.distanceTo(start) / (this.time - start.time)
45082             : 0;
45083         };
45084         return Point;
45085     }()),
45086     
45087     
45088     // Bezier Constructor
45089     Bezier: (function () {
45090         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45091             this.startPoint = startPoint;
45092             this.control2 = control2;
45093             this.control1 = control1;
45094             this.endPoint = endPoint;
45095             this.startWidth = startWidth;
45096             this.endWidth = endWidth;
45097         }
45098         Bezier.fromPoints = function (points, widths, scope) {
45099             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45100             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45101             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45102         };
45103         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45104             var dx1 = s1.x - s2.x;
45105             var dy1 = s1.y - s2.y;
45106             var dx2 = s2.x - s3.x;
45107             var dy2 = s2.y - s3.y;
45108             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45109             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45110             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45111             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45112             var dxm = m1.x - m2.x;
45113             var dym = m1.y - m2.y;
45114             var k = l2 / (l1 + l2);
45115             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45116             var tx = s2.x - cm.x;
45117             var ty = s2.y - cm.y;
45118             return {
45119                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45120                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45121             };
45122         };
45123         Bezier.prototype.length = function () {
45124             var steps = 10;
45125             var length = 0;
45126             var px;
45127             var py;
45128             for (var i = 0; i <= steps; i += 1) {
45129                 var t = i / steps;
45130                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45131                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45132                 if (i > 0) {
45133                     var xdiff = cx - px;
45134                     var ydiff = cy - py;
45135                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45136                 }
45137                 px = cx;
45138                 py = cy;
45139             }
45140             return length;
45141         };
45142         Bezier.prototype.point = function (t, start, c1, c2, end) {
45143             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45144             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45145             + (3.0 * c2 * (1.0 - t) * t * t)
45146             + (end * t * t * t);
45147         };
45148         return Bezier;
45149     }()),
45150     
45151     throttleStroke: function(fn, wait) {
45152       if (wait === void 0) { wait = 250; }
45153       var previous = 0;
45154       var timeout = null;
45155       var result;
45156       var storedContext;
45157       var storedArgs;
45158       var later = function () {
45159           previous = Date.now();
45160           timeout = null;
45161           result = fn.apply(storedContext, storedArgs);
45162           if (!timeout) {
45163               storedContext = null;
45164               storedArgs = [];
45165           }
45166       };
45167       return function wrapper() {
45168           var args = [];
45169           for (var _i = 0; _i < arguments.length; _i++) {
45170               args[_i] = arguments[_i];
45171           }
45172           var now = Date.now();
45173           var remaining = wait - (now - previous);
45174           storedContext = this;
45175           storedArgs = args;
45176           if (remaining <= 0 || remaining > wait) {
45177               if (timeout) {
45178                   clearTimeout(timeout);
45179                   timeout = null;
45180               }
45181               previous = now;
45182               result = fn.apply(storedContext, storedArgs);
45183               if (!timeout) {
45184                   storedContext = null;
45185                   storedArgs = [];
45186               }
45187           }
45188           else if (!timeout) {
45189               timeout = window.setTimeout(later, remaining);
45190           }
45191           return result;
45192       };
45193   }
45194   
45195 });
45196
45197  
45198
45199  // old names for form elements
45200 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
45201 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
45202 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
45203 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
45204 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
45205 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
45206 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
45207 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
45208 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
45209 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
45210 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
45211 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
45212 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
45213 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
45214 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
45215 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
45216 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
45217 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
45218 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
45219 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
45220 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
45221 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
45222 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
45223 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
45224 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
45225 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
45226
45227 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
45228 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45229
45230 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
45231 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
45232
45233 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
45234 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45235 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
45236 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
45237