major doc changes
[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  * @builder-top
859  * @children Roo.bootstrap.Component
860  * @parent none
861  * Bootstrap Body class
862  *
863  * @constructor
864  * Create a new body
865  * @param {Object} config The config object
866  */
867
868 Roo.bootstrap.Body = function(config){
869
870     config = config || {};
871
872     Roo.bootstrap.Body.superclass.constructor.call(this, config);
873     this.el = Roo.get(config.el ? config.el : document.body );
874     if (this.cls && this.cls.length) {
875         Roo.get(document.body).addClass(this.cls);
876     }
877 };
878
879 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
880
881     is_body : true,// just to make sure it's constructed?
882
883         autoCreate : {
884         cls: 'container'
885     },
886     onRender : function(ct, position)
887     {
888        /* Roo.log("Roo.bootstrap.Body - onRender");
889         if (this.cls && this.cls.length) {
890             Roo.get(document.body).addClass(this.cls);
891         }
892         // style??? xttr???
893         */
894     }
895
896
897
898
899 });
900 /*
901  * - LGPL
902  *
903  * button group
904  * 
905  */
906
907
908 /**
909  * @class Roo.bootstrap.ButtonGroup
910  * @extends Roo.bootstrap.Component
911  * Bootstrap ButtonGroup class
912  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
913  * 
914  * @cfg {String} size lg | sm | xs (default empty normal)
915  * @cfg {String} align vertical | justified  (default none)
916  * @cfg {String} direction up | down (default down)
917  * @cfg {Boolean} toolbar false | true
918  * @cfg {Boolean} btn true | false
919  * 
920  * 
921  * @constructor
922  * Create a new Input
923  * @param {Object} config The config object
924  */
925
926 Roo.bootstrap.ButtonGroup = function(config){
927     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
928 };
929
930 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
931     
932     size: '',
933     align: '',
934     direction: '',
935     toolbar: false,
936     btn: true,
937
938     getAutoCreate : function(){
939         var cfg = {
940             cls: 'btn-group',
941             html : null
942         };
943         
944         cfg.html = this.html || cfg.html;
945         
946         if (this.toolbar) {
947             cfg = {
948                 cls: 'btn-toolbar',
949                 html: null
950             };
951             
952             return cfg;
953         }
954         
955         if (['vertical','justified'].indexOf(this.align)!==-1) {
956             cfg.cls = 'btn-group-' + this.align;
957             
958             if (this.align == 'justified') {
959                 console.log(this.items);
960             }
961         }
962         
963         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
964             cfg.cls += ' btn-group-' + this.size;
965         }
966         
967         if (this.direction == 'up') {
968             cfg.cls += ' dropup' ;
969         }
970         
971         return cfg;
972     },
973     /**
974      * Add a button to the group (similar to NavItem API.)
975      */
976     addItem : function(cfg)
977     {
978         var cn = new Roo.bootstrap.Button(cfg);
979         //this.register(cn);
980         cn.parentId = this.id;
981         cn.onRender(this.el, null);
982         return cn;
983     }
984    
985 });
986
987  /*
988  * - LGPL
989  *
990  * button
991  * 
992  */
993
994 /**
995  * @class Roo.bootstrap.Button
996  * @extends Roo.bootstrap.Component
997  * Bootstrap Button class
998  * @cfg {String} html The button content
999  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
1000  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1001  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1002  * @cfg {String} size (lg|sm|xs)
1003  * @cfg {String} tag (a|input|submit)
1004  * @cfg {String} href empty or href
1005  * @cfg {Boolean} disabled default false;
1006  * @cfg {Boolean} isClose default false;
1007  * @cfg {String} glyphicon depricated - use fa
1008  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1009  * @cfg {String} badge text for badge
1010  * @cfg {String} theme (default|glow)  
1011  * @cfg {Boolean} inverse dark themed version
1012  * @cfg {Boolean} toggle is it a slidy toggle button
1013  * @cfg {Boolean} pressed   default null - if the button ahs active state
1014  * @cfg {String} ontext text for on slidy toggle state
1015  * @cfg {String} offtext text for off slidy toggle state
1016  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1017  * @cfg {Boolean} removeClass remove the standard class..
1018  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1019  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1020  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1021
1022  * @constructor
1023  * Create a new button
1024  * @param {Object} config The config object
1025  */
1026
1027
1028 Roo.bootstrap.Button = function(config){
1029     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1030     
1031     this.addEvents({
1032         // raw events
1033         /**
1034          * @event click
1035          * When a button is pressed
1036          * @param {Roo.bootstrap.Button} btn
1037          * @param {Roo.EventObject} e
1038          */
1039         "click" : true,
1040         /**
1041          * @event dblclick
1042          * When a button is double clicked
1043          * @param {Roo.bootstrap.Button} btn
1044          * @param {Roo.EventObject} e
1045          */
1046         "dblclick" : true,
1047          /**
1048          * @event toggle
1049          * After the button has been toggles
1050          * @param {Roo.bootstrap.Button} btn
1051          * @param {Roo.EventObject} e
1052          * @param {boolean} pressed (also available as button.pressed)
1053          */
1054         "toggle" : true
1055     });
1056 };
1057
1058 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1059     html: false,
1060     active: false,
1061     weight: '',
1062     badge_weight: '',
1063     outline : false,
1064     size: '',
1065     tag: 'button',
1066     href: '',
1067     disabled: false,
1068     isClose: false,
1069     glyphicon: '',
1070     fa: '',
1071     badge: '',
1072     theme: 'default',
1073     inverse: false,
1074     
1075     toggle: false,
1076     ontext: 'ON',
1077     offtext: 'OFF',
1078     defaulton: true,
1079     preventDefault: true,
1080     removeClass: false,
1081     name: false,
1082     target: false,
1083     group : false,
1084      
1085     pressed : null,
1086      
1087     
1088     getAutoCreate : function(){
1089         
1090         var cfg = {
1091             tag : 'button',
1092             cls : 'roo-button',
1093             html: ''
1094         };
1095         
1096         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1097             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1098             this.tag = 'button';
1099         } else {
1100             cfg.tag = this.tag;
1101         }
1102         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1103         
1104         if (this.toggle == true) {
1105             cfg={
1106                 tag: 'div',
1107                 cls: 'slider-frame roo-button',
1108                 cn: [
1109                     {
1110                         tag: 'span',
1111                         'data-on-text':'ON',
1112                         'data-off-text':'OFF',
1113                         cls: 'slider-button',
1114                         html: this.offtext
1115                     }
1116                 ]
1117             };
1118             // why are we validating the weights?
1119             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1120                 cfg.cls +=  ' ' + this.weight;
1121             }
1122             
1123             return cfg;
1124         }
1125         
1126         if (this.isClose) {
1127             cfg.cls += ' close';
1128             
1129             cfg["aria-hidden"] = true;
1130             
1131             cfg.html = "&times;";
1132             
1133             return cfg;
1134         }
1135              
1136         
1137         if (this.theme==='default') {
1138             cfg.cls = 'btn roo-button';
1139             
1140             //if (this.parentType != 'Navbar') {
1141             this.weight = this.weight.length ?  this.weight : 'default';
1142             //}
1143             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1144                 
1145                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1146                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1147                 cfg.cls += ' btn-' + outline + weight;
1148                 if (this.weight == 'default') {
1149                     // BC
1150                     cfg.cls += ' btn-' + this.weight;
1151                 }
1152             }
1153         } else if (this.theme==='glow') {
1154             
1155             cfg.tag = 'a';
1156             cfg.cls = 'btn-glow roo-button';
1157             
1158             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1159                 
1160                 cfg.cls += ' ' + this.weight;
1161             }
1162         }
1163    
1164         
1165         if (this.inverse) {
1166             this.cls += ' inverse';
1167         }
1168         
1169         
1170         if (this.active || this.pressed === true) {
1171             cfg.cls += ' active';
1172         }
1173         
1174         if (this.disabled) {
1175             cfg.disabled = 'disabled';
1176         }
1177         
1178         if (this.items) {
1179             Roo.log('changing to ul' );
1180             cfg.tag = 'ul';
1181             this.glyphicon = 'caret';
1182             if (Roo.bootstrap.version == 4) {
1183                 this.fa = 'caret-down';
1184             }
1185             
1186         }
1187         
1188         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1189          
1190         //gsRoo.log(this.parentType);
1191         if (this.parentType === 'Navbar' && !this.parent().bar) {
1192             Roo.log('changing to li?');
1193             
1194             cfg.tag = 'li';
1195             
1196             cfg.cls = '';
1197             cfg.cn =  [{
1198                 tag : 'a',
1199                 cls : 'roo-button',
1200                 html : this.html,
1201                 href : this.href || '#'
1202             }];
1203             if (this.menu) {
1204                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1205                 cfg.cls += ' dropdown';
1206             }   
1207             
1208             delete cfg.html;
1209             
1210         }
1211         
1212        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1213         
1214         if (this.glyphicon) {
1215             cfg.html = ' ' + cfg.html;
1216             
1217             cfg.cn = [
1218                 {
1219                     tag: 'span',
1220                     cls: 'glyphicon glyphicon-' + this.glyphicon
1221                 }
1222             ];
1223         }
1224         if (this.fa) {
1225             cfg.html = ' ' + cfg.html;
1226             
1227             cfg.cn = [
1228                 {
1229                     tag: 'i',
1230                     cls: 'fa fas fa-' + this.fa
1231                 }
1232             ];
1233         }
1234         
1235         if (this.badge) {
1236             cfg.html += ' ';
1237             
1238             cfg.tag = 'a';
1239             
1240 //            cfg.cls='btn roo-button';
1241             
1242             cfg.href=this.href;
1243             
1244             var value = cfg.html;
1245             
1246             if(this.glyphicon){
1247                 value = {
1248                     tag: 'span',
1249                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1250                     html: this.html
1251                 };
1252             }
1253             if(this.fa){
1254                 value = {
1255                     tag: 'i',
1256                     cls: 'fa fas fa-' + this.fa,
1257                     html: this.html
1258                 };
1259             }
1260             
1261             var bw = this.badge_weight.length ? this.badge_weight :
1262                 (this.weight.length ? this.weight : 'secondary');
1263             bw = bw == 'default' ? 'secondary' : bw;
1264             
1265             cfg.cn = [
1266                 value,
1267                 {
1268                     tag: 'span',
1269                     cls: 'badge badge-' + bw,
1270                     html: this.badge
1271                 }
1272             ];
1273             
1274             cfg.html='';
1275         }
1276         
1277         if (this.menu) {
1278             cfg.cls += ' dropdown';
1279             cfg.html = typeof(cfg.html) != 'undefined' ?
1280                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1281         }
1282         
1283         if (cfg.tag !== 'a' && this.href !== '') {
1284             throw "Tag must be a to set href.";
1285         } else if (this.href.length > 0) {
1286             cfg.href = this.href;
1287         }
1288         
1289         if(this.removeClass){
1290             cfg.cls = '';
1291         }
1292         
1293         if(this.target){
1294             cfg.target = this.target;
1295         }
1296         
1297         return cfg;
1298     },
1299     initEvents: function() {
1300        // Roo.log('init events?');
1301 //        Roo.log(this.el.dom);
1302         // add the menu...
1303         
1304         if (typeof (this.menu) != 'undefined') {
1305             this.menu.parentType = this.xtype;
1306             this.menu.triggerEl = this.el;
1307             this.addxtype(Roo.apply({}, this.menu));
1308         }
1309
1310
1311         if (this.el.hasClass('roo-button')) {
1312              this.el.on('click', this.onClick, this);
1313              this.el.on('dblclick', this.onDblClick, this);
1314         } else {
1315              this.el.select('.roo-button').on('click', this.onClick, this);
1316              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1317              
1318         }
1319         // why?
1320         if(this.removeClass){
1321             this.el.on('click', this.onClick, this);
1322         }
1323         
1324         if (this.group === true) {
1325              if (this.pressed === false || this.pressed === true) {
1326                 // nothing
1327             } else {
1328                 this.pressed = false;
1329                 this.setActive(this.pressed);
1330             }
1331             
1332         }
1333         
1334         this.el.enableDisplayMode();
1335         
1336     },
1337     onClick : function(e)
1338     {
1339         if (this.disabled) {
1340             return;
1341         }
1342         
1343         Roo.log('button on click ');
1344         if(this.preventDefault){
1345             e.preventDefault();
1346         }
1347         
1348         if (this.group) {
1349             if (this.pressed) {
1350                 // do nothing -
1351                 return;
1352             }
1353             this.setActive(true);
1354             var pi = this.parent().items;
1355             for (var i = 0;i < pi.length;i++) {
1356                 if (this == pi[i]) {
1357                     continue;
1358                 }
1359                 if (pi[i].el.hasClass('roo-button')) {
1360                     pi[i].setActive(false);
1361                 }
1362             }
1363             this.fireEvent('click', this, e);            
1364             return;
1365         }
1366         
1367         if (this.pressed === true || this.pressed === false) {
1368             this.toggleActive(e);
1369         }
1370         
1371         
1372         this.fireEvent('click', this, e);
1373     },
1374     onDblClick: function(e)
1375     {
1376         if (this.disabled) {
1377             return;
1378         }
1379         if(this.preventDefault){
1380             e.preventDefault();
1381         }
1382         this.fireEvent('dblclick', this, e);
1383     },
1384     /**
1385      * Enables this button
1386      */
1387     enable : function()
1388     {
1389         this.disabled = false;
1390         this.el.removeClass('disabled');
1391         this.el.dom.removeAttribute("disabled");
1392     },
1393     
1394     /**
1395      * Disable this button
1396      */
1397     disable : function()
1398     {
1399         this.disabled = true;
1400         this.el.addClass('disabled');
1401         this.el.attr("disabled", "disabled")
1402     },
1403      /**
1404      * sets the active state on/off, 
1405      * @param {Boolean} state (optional) Force a particular state
1406      */
1407     setActive : function(v) {
1408         
1409         this.el[v ? 'addClass' : 'removeClass']('active');
1410         this.pressed = v;
1411     },
1412      /**
1413      * toggles the current active state 
1414      */
1415     toggleActive : function(e)
1416     {
1417         this.setActive(!this.pressed); // this modifies pressed...
1418         this.fireEvent('toggle', this, e, this.pressed);
1419     },
1420      /**
1421      * get the current active state
1422      * @return {boolean} true if it's active
1423      */
1424     isActive : function()
1425     {
1426         return this.el.hasClass('active');
1427     },
1428     /**
1429      * set the text of the first selected button
1430      */
1431     setText : function(str)
1432     {
1433         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1434     },
1435     /**
1436      * get the text of the first selected button
1437      */
1438     getText : function()
1439     {
1440         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1441     },
1442     
1443     setWeight : function(str)
1444     {
1445         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1446         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1447         this.weight = str;
1448         var outline = this.outline ? 'outline-' : '';
1449         if (str == 'default') {
1450             this.el.addClass('btn-default btn-outline-secondary');        
1451             return;
1452         }
1453         this.el.addClass('btn-' + outline + str);        
1454     }
1455     
1456     
1457 });
1458 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1459
1460 Roo.bootstrap.Button.weights = [
1461     'default',
1462     'secondary' ,
1463     'primary',
1464     'success',
1465     'info',
1466     'warning',
1467     'danger',
1468     'link',
1469     'light',
1470     'dark'              
1471    
1472 ];/*
1473  * - LGPL
1474  *
1475  * column
1476  * 
1477  */
1478
1479 /**
1480  * @class Roo.bootstrap.Column
1481  * @extends Roo.bootstrap.Component
1482  * @children Roo.bootstrap.Component
1483  * Bootstrap Column class
1484  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1485  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1486  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1487  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1488  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1489  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1490  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1491  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1492  *
1493  * 
1494  * @cfg {Boolean} hidden (true|false) hide the element
1495  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1496  * @cfg {String} fa (ban|check|...) font awesome icon
1497  * @cfg {Number} fasize (1|2|....) font awsome size
1498
1499  * @cfg {String} icon (info-sign|check|...) glyphicon name
1500
1501  * @cfg {String} html content of column.
1502  * 
1503  * @constructor
1504  * Create a new Column
1505  * @param {Object} config The config object
1506  */
1507
1508 Roo.bootstrap.Column = function(config){
1509     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1510 };
1511
1512 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1513     
1514     xs: false,
1515     sm: false,
1516     md: false,
1517     lg: false,
1518     xsoff: false,
1519     smoff: false,
1520     mdoff: false,
1521     lgoff: false,
1522     html: '',
1523     offset: 0,
1524     alert: false,
1525     fa: false,
1526     icon : false,
1527     hidden : false,
1528     fasize : 1,
1529     
1530     getAutoCreate : function(){
1531         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1532         
1533         cfg = {
1534             tag: 'div',
1535             cls: 'column'
1536         };
1537         
1538         var settings=this;
1539         var sizes =   ['xs','sm','md','lg'];
1540         sizes.map(function(size ,ix){
1541             //Roo.log( size + ':' + settings[size]);
1542             
1543             if (settings[size+'off'] !== false) {
1544                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1545             }
1546             
1547             if (settings[size] === false) {
1548                 return;
1549             }
1550             
1551             if (!settings[size]) { // 0 = hidden
1552                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1553                 // bootsrap4
1554                 for (var i = ix; i > -1; i--) {
1555                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1556                 }
1557                 
1558                 
1559                 return;
1560             }
1561             cfg.cls += ' col-' + size + '-' + settings[size] + (
1562                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1563             );
1564             
1565         });
1566         
1567         if (this.hidden) {
1568             cfg.cls += ' hidden';
1569         }
1570         
1571         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1572             cfg.cls +=' alert alert-' + this.alert;
1573         }
1574         
1575         
1576         if (this.html.length) {
1577             cfg.html = this.html;
1578         }
1579         if (this.fa) {
1580             var fasize = '';
1581             if (this.fasize > 1) {
1582                 fasize = ' fa-' + this.fasize + 'x';
1583             }
1584             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1585             
1586             
1587         }
1588         if (this.icon) {
1589             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1590         }
1591         
1592         return cfg;
1593     }
1594    
1595 });
1596
1597  
1598
1599  /*
1600  * - LGPL
1601  *
1602  * page container.
1603  * 
1604  */
1605
1606
1607 /**
1608  * @class Roo.bootstrap.Container
1609  * @extends Roo.bootstrap.Component
1610  * @builder-top
1611  * @children Roo.bootstrap.Component
1612  * Bootstrap Container class
1613  * @cfg {Boolean} jumbotron is it a jumbotron element
1614  * @cfg {String} html content of element
1615  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1616  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1617  * @cfg {String} header content of header (for panel)
1618  * @cfg {String} footer content of footer (for panel)
1619  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1620  * @cfg {String} tag (header|aside|section) type of HTML tag.
1621  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1622  * @cfg {String} fa font awesome icon
1623  * @cfg {String} icon (info-sign|check|...) glyphicon name
1624  * @cfg {Boolean} hidden (true|false) hide the element
1625  * @cfg {Boolean} expandable (true|false) default false
1626  * @cfg {Boolean} expanded (true|false) default true
1627  * @cfg {String} rheader contet on the right of header
1628  * @cfg {Boolean} clickable (true|false) default false
1629
1630  *     
1631  * @constructor
1632  * Create a new Container
1633  * @param {Object} config The config object
1634  */
1635
1636 Roo.bootstrap.Container = function(config){
1637     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1638     
1639     this.addEvents({
1640         // raw events
1641          /**
1642          * @event expand
1643          * After the panel has been expand
1644          * 
1645          * @param {Roo.bootstrap.Container} this
1646          */
1647         "expand" : true,
1648         /**
1649          * @event collapse
1650          * After the panel has been collapsed
1651          * 
1652          * @param {Roo.bootstrap.Container} this
1653          */
1654         "collapse" : true,
1655         /**
1656          * @event click
1657          * When a element is chick
1658          * @param {Roo.bootstrap.Container} this
1659          * @param {Roo.EventObject} e
1660          */
1661         "click" : true
1662     });
1663 };
1664
1665 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1666     
1667     jumbotron : false,
1668     well: '',
1669     panel : '',
1670     header: '',
1671     footer : '',
1672     sticky: '',
1673     tag : false,
1674     alert : false,
1675     fa: false,
1676     icon : false,
1677     expandable : false,
1678     rheader : '',
1679     expanded : true,
1680     clickable: false,
1681   
1682      
1683     getChildContainer : function() {
1684         
1685         if(!this.el){
1686             return false;
1687         }
1688         
1689         if (this.panel.length) {
1690             return this.el.select('.panel-body',true).first();
1691         }
1692         
1693         return this.el;
1694     },
1695     
1696     
1697     getAutoCreate : function(){
1698         
1699         var cfg = {
1700             tag : this.tag || 'div',
1701             html : '',
1702             cls : ''
1703         };
1704         if (this.jumbotron) {
1705             cfg.cls = 'jumbotron';
1706         }
1707         
1708         
1709         
1710         // - this is applied by the parent..
1711         //if (this.cls) {
1712         //    cfg.cls = this.cls + '';
1713         //}
1714         
1715         if (this.sticky.length) {
1716             
1717             var bd = Roo.get(document.body);
1718             if (!bd.hasClass('bootstrap-sticky')) {
1719                 bd.addClass('bootstrap-sticky');
1720                 Roo.select('html',true).setStyle('height', '100%');
1721             }
1722              
1723             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1724         }
1725         
1726         
1727         if (this.well.length) {
1728             switch (this.well) {
1729                 case 'lg':
1730                 case 'sm':
1731                     cfg.cls +=' well well-' +this.well;
1732                     break;
1733                 default:
1734                     cfg.cls +=' well';
1735                     break;
1736             }
1737         }
1738         
1739         if (this.hidden) {
1740             cfg.cls += ' hidden';
1741         }
1742         
1743         
1744         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1745             cfg.cls +=' alert alert-' + this.alert;
1746         }
1747         
1748         var body = cfg;
1749         
1750         if (this.panel.length) {
1751             cfg.cls += ' panel panel-' + this.panel;
1752             cfg.cn = [];
1753             if (this.header.length) {
1754                 
1755                 var h = [];
1756                 
1757                 if(this.expandable){
1758                     
1759                     cfg.cls = cfg.cls + ' expandable';
1760                     
1761                     h.push({
1762                         tag: 'i',
1763                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1764                     });
1765                     
1766                 }
1767                 
1768                 h.push(
1769                     {
1770                         tag: 'span',
1771                         cls : 'panel-title',
1772                         html : (this.expandable ? '&nbsp;' : '') + this.header
1773                     },
1774                     {
1775                         tag: 'span',
1776                         cls: 'panel-header-right',
1777                         html: this.rheader
1778                     }
1779                 );
1780                 
1781                 cfg.cn.push({
1782                     cls : 'panel-heading',
1783                     style : this.expandable ? 'cursor: pointer' : '',
1784                     cn : h
1785                 });
1786                 
1787             }
1788             
1789             body = false;
1790             cfg.cn.push({
1791                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1792                 html : this.html
1793             });
1794             
1795             
1796             if (this.footer.length) {
1797                 cfg.cn.push({
1798                     cls : 'panel-footer',
1799                     html : this.footer
1800                     
1801                 });
1802             }
1803             
1804         }
1805         
1806         if (body) {
1807             body.html = this.html || cfg.html;
1808             // prefix with the icons..
1809             if (this.fa) {
1810                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1811             }
1812             if (this.icon) {
1813                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1814             }
1815             
1816             
1817         }
1818         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1819             cfg.cls =  'container';
1820         }
1821         
1822         return cfg;
1823     },
1824     
1825     initEvents: function() 
1826     {
1827         if(this.expandable){
1828             var headerEl = this.headerEl();
1829         
1830             if(headerEl){
1831                 headerEl.on('click', this.onToggleClick, this);
1832             }
1833         }
1834         
1835         if(this.clickable){
1836             this.el.on('click', this.onClick, this);
1837         }
1838         
1839     },
1840     
1841     onToggleClick : function()
1842     {
1843         var headerEl = this.headerEl();
1844         
1845         if(!headerEl){
1846             return;
1847         }
1848         
1849         if(this.expanded){
1850             this.collapse();
1851             return;
1852         }
1853         
1854         this.expand();
1855     },
1856     
1857     expand : function()
1858     {
1859         if(this.fireEvent('expand', this)) {
1860             
1861             this.expanded = true;
1862             
1863             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1864             
1865             this.el.select('.panel-body',true).first().removeClass('hide');
1866             
1867             var toggleEl = this.toggleEl();
1868
1869             if(!toggleEl){
1870                 return;
1871             }
1872
1873             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1874         }
1875         
1876     },
1877     
1878     collapse : function()
1879     {
1880         if(this.fireEvent('collapse', this)) {
1881             
1882             this.expanded = false;
1883             
1884             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1885             this.el.select('.panel-body',true).first().addClass('hide');
1886         
1887             var toggleEl = this.toggleEl();
1888
1889             if(!toggleEl){
1890                 return;
1891             }
1892
1893             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1894         }
1895     },
1896     
1897     toggleEl : function()
1898     {
1899         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1900             return;
1901         }
1902         
1903         return this.el.select('.panel-heading .fa',true).first();
1904     },
1905     
1906     headerEl : function()
1907     {
1908         if(!this.el || !this.panel.length || !this.header.length){
1909             return;
1910         }
1911         
1912         return this.el.select('.panel-heading',true).first()
1913     },
1914     
1915     bodyEl : function()
1916     {
1917         if(!this.el || !this.panel.length){
1918             return;
1919         }
1920         
1921         return this.el.select('.panel-body',true).first()
1922     },
1923     
1924     titleEl : function()
1925     {
1926         if(!this.el || !this.panel.length || !this.header.length){
1927             return;
1928         }
1929         
1930         return this.el.select('.panel-title',true).first();
1931     },
1932     
1933     setTitle : function(v)
1934     {
1935         var titleEl = this.titleEl();
1936         
1937         if(!titleEl){
1938             return;
1939         }
1940         
1941         titleEl.dom.innerHTML = v;
1942     },
1943     
1944     getTitle : function()
1945     {
1946         
1947         var titleEl = this.titleEl();
1948         
1949         if(!titleEl){
1950             return '';
1951         }
1952         
1953         return titleEl.dom.innerHTML;
1954     },
1955     
1956     setRightTitle : function(v)
1957     {
1958         var t = this.el.select('.panel-header-right',true).first();
1959         
1960         if(!t){
1961             return;
1962         }
1963         
1964         t.dom.innerHTML = v;
1965     },
1966     
1967     onClick : function(e)
1968     {
1969         e.preventDefault();
1970         
1971         this.fireEvent('click', this, e);
1972     }
1973 });
1974
1975  /**
1976  * @class Roo.bootstrap.Card
1977  * @extends Roo.bootstrap.Component
1978  * @children Roo.bootstrap.Component
1979  * @licence LGPL
1980  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1981  *
1982  *
1983  * possible... may not be implemented..
1984  * @cfg {String} header_image  src url of image.
1985  * @cfg {String|Object} header
1986  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1987  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1988  * 
1989  * @cfg {String} title
1990  * @cfg {String} subtitle
1991  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1992  * @cfg {String} footer
1993  
1994  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1995  * 
1996  * @cfg {String} margin (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2002  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2003  *
2004  * @cfg {String} padding (0|1|2|3|4|5)
2005  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2006  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2007  * @cfg {String} padding_left (0|1|2|3|4|5)
2008  * @cfg {String} padding_right (0|1|2|3|4|5)
2009  * @cfg {String} padding_x (0|1|2|3|4|5)
2010  * @cfg {String} padding_y (0|1|2|3|4|5)
2011  *
2012  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2017  
2018  * @config {Boolean} dragable  if this card can be dragged.
2019  * @config {String} drag_group  group for drag
2020  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2021  * @config {String} drop_group  group for drag
2022  * 
2023  * @config {Boolean} collapsable can the body be collapsed.
2024  * @config {Boolean} collapsed is the body collapsed when rendered...
2025  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2026  * @config {Boolean} rotated is the body rotated when rendered...
2027  * 
2028  * @constructor
2029  * Create a new Container
2030  * @param {Object} config The config object
2031  */
2032
2033 Roo.bootstrap.Card = function(config){
2034     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2035     
2036     this.addEvents({
2037          // raw events
2038         /**
2039          * @event drop
2040          * When a element a card is dropped
2041          * @param {Roo.bootstrap.Card} this
2042          *
2043          * 
2044          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2045          * @param {String} position 'above' or 'below'
2046          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2047         
2048          */
2049         'drop' : true,
2050          /**
2051          * @event rotate
2052          * When a element a card is rotate
2053          * @param {Roo.bootstrap.Card} this
2054          * @param {Roo.Element} n the node being dropped?
2055          * @param {Boolean} rotate status
2056          */
2057         'rotate' : true,
2058         /**
2059          * @event cardover
2060          * When a card element is dragged over ready to drop (return false to block dropable)
2061          * @param {Roo.bootstrap.Card} this
2062          * @param {Object} data from dragdrop 
2063          */
2064          'cardover' : true
2065          
2066     });
2067 };
2068
2069
2070 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2071     
2072     
2073     weight : '',
2074     
2075     margin: '', /// may be better in component?
2076     margin_top: '', 
2077     margin_bottom: '', 
2078     margin_left: '',
2079     margin_right: '',
2080     margin_x: '',
2081     margin_y: '',
2082     
2083     padding : '',
2084     padding_top: '', 
2085     padding_bottom: '', 
2086     padding_left: '',
2087     padding_right: '',
2088     padding_x: '',
2089     padding_y: '',
2090     
2091     display: '', 
2092     display_xs: '', 
2093     display_sm: '', 
2094     display_lg: '',
2095     display_xl: '',
2096  
2097     header_image  : '',
2098     header : '',
2099     header_size : 0,
2100     title : '',
2101     subtitle : '',
2102     html : '',
2103     footer: '',
2104
2105     collapsable : false,
2106     collapsed : false,
2107     rotateable : false,
2108     rotated : false,
2109     
2110     dragable : false,
2111     drag_group : false,
2112     dropable : false,
2113     drop_group : false,
2114     childContainer : false,
2115     dropEl : false, /// the dom placeholde element that indicates drop location.
2116     containerEl: false, // body container
2117     bodyEl: false, // card-body
2118     headerContainerEl : false, //
2119     headerEl : false,
2120     header_imageEl : false,
2121     
2122     
2123     layoutCls : function()
2124     {
2125         var cls = '';
2126         var t = this;
2127         Roo.log(this.margin_bottom.length);
2128         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2129             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2130             
2131             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2132                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2133             }
2134             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2135                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2136             }
2137         });
2138         
2139         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2140             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2141                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2142             }
2143         });
2144         
2145         // more generic support?
2146         if (this.hidden) {
2147             cls += ' d-none';
2148         }
2149         
2150         return cls;
2151     },
2152  
2153        // Roo.log("Call onRender: " + this.xtype);
2154         /*  We are looking at something like this.
2155 <div class="card">
2156     <img src="..." class="card-img-top" alt="...">
2157     <div class="card-body">
2158         <h5 class="card-title">Card title</h5>
2159          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2160
2161         >> this bit is really the body...
2162         <div> << we will ad dthis in hopefully it will not break shit.
2163         
2164         ** card text does not actually have any styling...
2165         
2166             <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>
2167         
2168         </div> <<
2169           <a href="#" class="card-link">Card link</a>
2170           
2171     </div>
2172     <div class="card-footer">
2173         <small class="text-muted">Last updated 3 mins ago</small>
2174     </div>
2175 </div>
2176          */
2177     getAutoCreate : function(){
2178         
2179         var cfg = {
2180             tag : 'div',
2181             cls : 'card',
2182             cn : [ ]
2183         };
2184         
2185         if (this.weight.length && this.weight != 'light') {
2186             cfg.cls += ' text-white';
2187         } else {
2188             cfg.cls += ' text-dark'; // need as it's nested..
2189         }
2190         if (this.weight.length) {
2191             cfg.cls += ' bg-' + this.weight;
2192         }
2193         
2194         cfg.cls += ' ' + this.layoutCls(); 
2195         
2196         var hdr = false;
2197         var hdr_ctr = false;
2198         if (this.header.length) {
2199             hdr = {
2200                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2201                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202                 cn : []
2203             };
2204             cfg.cn.push(hdr);
2205             hdr_ctr = hdr;
2206         } else {
2207             hdr = {
2208                 tag : 'div',
2209                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2210                 cn : []
2211             };
2212             cfg.cn.push(hdr);
2213             hdr_ctr = hdr;
2214         }
2215         if (this.collapsable) {
2216             hdr_ctr = {
2217             tag : 'a',
2218             cls : 'd-block user-select-none',
2219             cn: [
2220                     {
2221                         tag: 'i',
2222                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2223                     }
2224                    
2225                 ]
2226             };
2227             hdr.cn.push(hdr_ctr);
2228         }
2229         
2230         hdr_ctr.cn.push(        {
2231             tag: 'span',
2232             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2233             html : this.header
2234         });
2235         
2236         
2237         if (this.header_image.length) {
2238             cfg.cn.push({
2239                 tag : 'img',
2240                 cls : 'card-img-top',
2241                 src: this.header_image // escape?
2242             });
2243         } else {
2244             cfg.cn.push({
2245                     tag : 'div',
2246                     cls : 'card-img-top d-none' 
2247                 });
2248         }
2249             
2250         var body = {
2251             tag : 'div',
2252             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2253             cn : []
2254         };
2255         var obody = body;
2256         if (this.collapsable || this.rotateable) {
2257             obody = {
2258                 tag: 'div',
2259                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2260                 cn : [  body ]
2261             };
2262         }
2263         
2264         cfg.cn.push(obody);
2265         
2266         if (this.title.length) {
2267             body.cn.push({
2268                 tag : 'div',
2269                 cls : 'card-title',
2270                 src: this.title // escape?
2271             });
2272         }  
2273         
2274         if (this.subtitle.length) {
2275             body.cn.push({
2276                 tag : 'div',
2277                 cls : 'card-title',
2278                 src: this.subtitle // escape?
2279             });
2280         }
2281         
2282         body.cn.push({
2283             tag : 'div',
2284             cls : 'roo-card-body-ctr'
2285         });
2286         
2287         if (this.html.length) {
2288             body.cn.push({
2289                 tag: 'div',
2290                 html : this.html
2291             });
2292         }
2293         // fixme ? handle objects?
2294         
2295         if (this.footer.length) {
2296            
2297             cfg.cn.push({
2298                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2299                 html : this.footer
2300             });
2301             
2302         } else {
2303             cfg.cn.push({cls : 'card-footer d-none'});
2304         }
2305         
2306         // footer...
2307         
2308         return cfg;
2309     },
2310     
2311     
2312     getCardHeader : function()
2313     {
2314         var  ret = this.el.select('.card-header',true).first();
2315         if (ret.hasClass('d-none')) {
2316             ret.removeClass('d-none');
2317         }
2318         
2319         return ret;
2320     },
2321     getCardFooter : function()
2322     {
2323         var  ret = this.el.select('.card-footer',true).first();
2324         if (ret.hasClass('d-none')) {
2325             ret.removeClass('d-none');
2326         }
2327         
2328         return ret;
2329     },
2330     getCardImageTop : function()
2331     {
2332         var  ret = this.header_imageEl;
2333         if (ret.hasClass('d-none')) {
2334             ret.removeClass('d-none');
2335         }
2336             
2337         return ret;
2338     },
2339     
2340     getChildContainer : function()
2341     {
2342         
2343         if(!this.el){
2344             return false;
2345         }
2346         return this.el.select('.roo-card-body-ctr',true).first();    
2347     },
2348     
2349     initEvents: function() 
2350     {
2351         this.bodyEl = this.el.select('.card-body',true).first(); 
2352         this.containerEl = this.getChildContainer();
2353         if(this.dragable){
2354             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2355                     containerScroll: true,
2356                     ddGroup: this.drag_group || 'default_card_drag_group'
2357             });
2358             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2359         }
2360         if (this.dropable) {
2361             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2362                 containerScroll: true,
2363                 ddGroup: this.drop_group || 'default_card_drag_group'
2364             });
2365             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2366             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2367             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2368             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2369             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2370         }
2371         
2372         if (this.collapsable) {
2373             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2374         }
2375         if (this.rotateable) {
2376             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2377         }
2378         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2379          
2380         this.footerEl = this.el.select('.card-footer',true).first();
2381         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2382         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2383         this.headerEl = this.el.select('.card-header',true).first();
2384         
2385         if (this.rotated) {
2386             this.el.addClass('roo-card-rotated');
2387             this.fireEvent('rotate', this, true);
2388         }
2389         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2390         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2391         
2392     },
2393     getDragData : function(e)
2394     {
2395         var target = this.getEl();
2396         if (target) {
2397             //this.handleSelection(e);
2398             
2399             var dragData = {
2400                 source: this,
2401                 copy: false,
2402                 nodes: this.getEl(),
2403                 records: []
2404             };
2405             
2406             
2407             dragData.ddel = target.dom ;    // the div element
2408             Roo.log(target.getWidth( ));
2409             dragData.ddel.style.width = target.getWidth() + 'px';
2410             
2411             return dragData;
2412         }
2413         return false;
2414     },
2415     /**
2416     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2417     *    whole Element becomes the target, and this causes the drop gesture to append.
2418     *
2419     *    Returns an object:
2420     *     {
2421            
2422            position : 'below' or 'above'
2423            card  : relateive to card OBJECT (or true for no cards listed)
2424            items_n : relative to nth item in list
2425            card_n : relative to  nth card in list
2426     }
2427     *
2428     *    
2429     */
2430     getTargetFromEvent : function(e, dragged_card_el)
2431     {
2432         var target = e.getTarget();
2433         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2434             target = target.parentNode;
2435         }
2436         
2437         var ret = {
2438             position: '',
2439             cards : [],
2440             card_n : -1,
2441             items_n : -1,
2442             card : false 
2443         };
2444         
2445         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2446         // see if target is one of the 'cards'...
2447         
2448         
2449         //Roo.log(this.items.length);
2450         var pos = false;
2451         
2452         var last_card_n = 0;
2453         var cards_len  = 0;
2454         for (var i = 0;i< this.items.length;i++) {
2455             
2456             if (!this.items[i].el.hasClass('card')) {
2457                  continue;
2458             }
2459             pos = this.getDropPoint(e, this.items[i].el.dom);
2460             
2461             cards_len = ret.cards.length;
2462             //Roo.log(this.items[i].el.dom.id);
2463             ret.cards.push(this.items[i]);
2464             last_card_n  = i;
2465             if (ret.card_n < 0 && pos == 'above') {
2466                 ret.position = cards_len > 0 ? 'below' : pos;
2467                 ret.items_n = i > 0 ? i - 1 : 0;
2468                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2469                 ret.card = ret.cards[ret.card_n];
2470             }
2471         }
2472         if (!ret.cards.length) {
2473             ret.card = true;
2474             ret.position = 'below';
2475             ret.items_n;
2476             return ret;
2477         }
2478         // could not find a card.. stick it at the end..
2479         if (ret.card_n < 0) {
2480             ret.card_n = last_card_n;
2481             ret.card = ret.cards[last_card_n];
2482             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2483             ret.position = 'below';
2484         }
2485         
2486         if (this.items[ret.items_n].el == dragged_card_el) {
2487             return false;
2488         }
2489         
2490         if (ret.position == 'below') {
2491             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2492             
2493             if (card_after  && card_after.el == dragged_card_el) {
2494                 return false;
2495             }
2496             return ret;
2497         }
2498         
2499         // its's after ..
2500         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2501         
2502         if (card_before  && card_before.el == dragged_card_el) {
2503             return false;
2504         }
2505         
2506         return ret;
2507     },
2508     
2509     onNodeEnter : function(n, dd, e, data){
2510         return false;
2511     },
2512     onNodeOver : function(n, dd, e, data)
2513     {
2514        
2515         var target_info = this.getTargetFromEvent(e,data.source.el);
2516         if (target_info === false) {
2517             this.dropPlaceHolder('hide');
2518             return false;
2519         }
2520         Roo.log(['getTargetFromEvent', target_info ]);
2521         
2522         
2523         if (this.fireEvent('cardover', this, [ data ]) === false) {
2524             return false;
2525         }
2526         
2527         this.dropPlaceHolder('show', target_info,data);
2528         
2529         return false; 
2530     },
2531     onNodeOut : function(n, dd, e, data){
2532         this.dropPlaceHolder('hide');
2533      
2534     },
2535     onNodeDrop : function(n, dd, e, data)
2536     {
2537         
2538         // call drop - return false if
2539         
2540         // this could actually fail - if the Network drops..
2541         // we will ignore this at present..- client should probably reload
2542         // the whole set of cards if stuff like that fails.
2543         
2544         
2545         var info = this.getTargetFromEvent(e,data.source.el);
2546         if (info === false) {
2547             return false;
2548         }
2549         this.dropPlaceHolder('hide');
2550   
2551           
2552     
2553         this.acceptCard(data.source, info.position, info.card, info.items_n);
2554         return true;
2555          
2556     },
2557     firstChildCard : function()
2558     {
2559         for (var i = 0;i< this.items.length;i++) {
2560             
2561             if (!this.items[i].el.hasClass('card')) {
2562                  continue;
2563             }
2564             return this.items[i];
2565         }
2566         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2567     },
2568     /**
2569      * accept card
2570      *
2571      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2572      */
2573     acceptCard : function(move_card,  position, next_to_card )
2574     {
2575         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2576             return false;
2577         }
2578         
2579         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2580         
2581         move_card.parent().removeCard(move_card);
2582         
2583         
2584         var dom = move_card.el.dom;
2585         dom.style.width = ''; // clear with - which is set by drag.
2586         
2587         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2588             var cardel = next_to_card.el.dom;
2589             
2590             if (position == 'above' ) {
2591                 cardel.parentNode.insertBefore(dom, cardel);
2592             } else if (cardel.nextSibling) {
2593                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2594             } else {
2595                 cardel.parentNode.append(dom);
2596             }
2597         } else {
2598             // card container???
2599             this.containerEl.dom.append(dom);
2600         }
2601         
2602         //FIXME HANDLE card = true 
2603         
2604         // add this to the correct place in items.
2605         
2606         // remove Card from items.
2607         
2608        
2609         if (this.items.length) {
2610             var nitems = [];
2611             //Roo.log([info.items_n, info.position, this.items.length]);
2612             for (var i =0; i < this.items.length; i++) {
2613                 if (i == to_items_n && position == 'above') {
2614                     nitems.push(move_card);
2615                 }
2616                 nitems.push(this.items[i]);
2617                 if (i == to_items_n && position == 'below') {
2618                     nitems.push(move_card);
2619                 }
2620             }
2621             this.items = nitems;
2622             Roo.log(this.items);
2623         } else {
2624             this.items.push(move_card);
2625         }
2626         
2627         move_card.parentId = this.id;
2628         
2629         return true;
2630         
2631         
2632     },
2633     removeCard : function(c)
2634     {
2635         this.items = this.items.filter(function(e) { return e != c });
2636  
2637         var dom = c.el.dom;
2638         dom.parentNode.removeChild(dom);
2639         dom.style.width = ''; // clear with - which is set by drag.
2640         c.parentId = false;
2641         
2642     },
2643     
2644     /**    Decide whether to drop above or below a View node. */
2645     getDropPoint : function(e, n, dd)
2646     {
2647         if (dd) {
2648              return false;
2649         }
2650         if (n == this.containerEl.dom) {
2651             return "above";
2652         }
2653         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2654         var c = t + (b - t) / 2;
2655         var y = Roo.lib.Event.getPageY(e);
2656         if(y <= c) {
2657             return "above";
2658         }else{
2659             return "below";
2660         }
2661     },
2662     onToggleCollapse : function(e)
2663         {
2664         if (this.collapsed) {
2665             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2666             this.collapsableEl.addClass('show');
2667             this.collapsed = false;
2668             return;
2669         }
2670         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2671         this.collapsableEl.removeClass('show');
2672         this.collapsed = true;
2673         
2674     
2675     },
2676     
2677     onToggleRotate : function(e)
2678     {
2679         this.collapsableEl.removeClass('show');
2680         this.footerEl.removeClass('d-none');
2681         this.el.removeClass('roo-card-rotated');
2682         this.el.removeClass('d-none');
2683         if (this.rotated) {
2684             
2685             this.collapsableEl.addClass('show');
2686             this.rotated = false;
2687             this.fireEvent('rotate', this, this.rotated);
2688             return;
2689         }
2690         this.el.addClass('roo-card-rotated');
2691         this.footerEl.addClass('d-none');
2692         this.el.select('.roo-collapsable').removeClass('show');
2693         
2694         this.rotated = true;
2695         this.fireEvent('rotate', this, this.rotated);
2696     
2697     },
2698     
2699     dropPlaceHolder: function (action, info, data)
2700     {
2701         if (this.dropEl === false) {
2702             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2703             cls : 'd-none'
2704             },true);
2705         }
2706         this.dropEl.removeClass(['d-none', 'd-block']);        
2707         if (action == 'hide') {
2708             
2709             this.dropEl.addClass('d-none');
2710             return;
2711         }
2712         // FIXME - info.card == true!!!
2713         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2714         
2715         if (info.card !== true) {
2716             var cardel = info.card.el.dom;
2717             
2718             if (info.position == 'above') {
2719                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2720             } else if (cardel.nextSibling) {
2721                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2722             } else {
2723                 cardel.parentNode.append(this.dropEl.dom);
2724             }
2725         } else {
2726             // card container???
2727             this.containerEl.dom.append(this.dropEl.dom);
2728         }
2729         
2730         this.dropEl.addClass('d-block roo-card-dropzone');
2731         
2732         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2733         
2734         
2735     
2736     
2737     
2738     },
2739     setHeaderText: function(html)
2740     {
2741         this.header = html;
2742         if (this.headerContainerEl) {
2743             this.headerContainerEl.dom.innerHTML = html;
2744         }
2745     },
2746     onHeaderImageLoad : function(ev, he)
2747     {
2748         if (!this.header_image_fit_square) {
2749             return;
2750         }
2751         
2752         var hw = he.naturalHeight / he.naturalWidth;
2753         // wide image = < 0
2754         // tall image = > 1
2755         //var w = he.dom.naturalWidth;
2756         var ww = he.width;
2757         he.style.left =  0;
2758         he.style.position =  'relative';
2759         if (hw > 1) {
2760             var nw = (ww * (1/hw));
2761             Roo.get(he).setSize( ww * (1/hw),  ww);
2762             he.style.left =  ((ww - nw)/ 2) + 'px';
2763             he.style.position =  'relative';
2764         }
2765
2766     }
2767
2768     
2769 });
2770
2771 /*
2772  * - LGPL
2773  *
2774  * Card header - holder for the card header elements.
2775  * 
2776  */
2777
2778 /**
2779  * @class Roo.bootstrap.CardHeader
2780  * @extends Roo.bootstrap.Element
2781  * @parent Roo.bootstrap.Card
2782  * @children Roo.bootstrap.Component
2783  * Bootstrap CardHeader class
2784  * @constructor
2785  * Create a new Card Header - that you can embed children into
2786  * @param {Object} config The config object
2787  */
2788
2789 Roo.bootstrap.CardHeader = function(config){
2790     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2791 };
2792
2793 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2794     
2795     
2796     container_method : 'getCardHeader' 
2797     
2798      
2799     
2800     
2801    
2802 });
2803
2804  
2805
2806  /*
2807  * - LGPL
2808  *
2809  * Card footer - holder for the card footer elements.
2810  * 
2811  */
2812
2813 /**
2814  * @class Roo.bootstrap.CardFooter
2815  * @extends Roo.bootstrap.Element
2816  * @parent Roo.bootstrap.Card
2817  * @children Roo.bootstrap.Component
2818  * Bootstrap CardFooter class
2819  * 
2820  * @constructor
2821  * Create a new Card Footer - that you can embed children into
2822  * @param {Object} config The config object
2823  */
2824
2825 Roo.bootstrap.CardFooter = function(config){
2826     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2827 };
2828
2829 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2830     
2831     
2832     container_method : 'getCardFooter' 
2833     
2834      
2835     
2836     
2837    
2838 });
2839
2840  
2841
2842  /*
2843  * - LGPL
2844  *
2845  * Card header - holder for the card header elements.
2846  * 
2847  */
2848
2849 /**
2850  * @class Roo.bootstrap.CardImageTop
2851  * @extends Roo.bootstrap.Element
2852  * @parent Roo.bootstrap.Card
2853  * @children Roo.bootstrap.Component
2854  * Bootstrap CardImageTop class
2855  * 
2856  * @constructor
2857  * Create a new Card Image Top container
2858  * @param {Object} config The config object
2859  */
2860
2861 Roo.bootstrap.CardImageTop = function(config){
2862     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2863 };
2864
2865 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2866     
2867    
2868     container_method : 'getCardImageTop' 
2869     
2870      
2871     
2872    
2873 });
2874
2875  
2876
2877  
2878 /*
2879 * Licence: LGPL
2880 */
2881
2882 /**
2883  * @class Roo.bootstrap.ButtonUploader
2884  * @extends Roo.bootstrap.Button
2885  * Bootstrap Button Uploader class - it's a button which when you add files to it
2886  *
2887  * 
2888  * @cfg {Number} errorTimeout default 3000
2889  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2890  * @cfg {Array}  html The button text.
2891  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2892  *
2893  * @constructor
2894  * Create a new CardUploader
2895  * @param {Object} config The config object
2896  */
2897
2898 Roo.bootstrap.ButtonUploader = function(config){
2899     
2900  
2901     
2902     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2903     
2904      
2905      this.addEvents({
2906          // raw events
2907         /**
2908          * @event beforeselect
2909          * When button is pressed, before show upload files dialog is shown
2910          * @param {Roo.bootstrap.UploaderButton} this
2911          *
2912          */
2913         'beforeselect' : true,
2914          /**
2915          * @event fired when files have been selected, 
2916          * When a the download link is clicked
2917          * @param {Roo.bootstrap.UploaderButton} this
2918          * @param {Array} Array of files that have been uploaded
2919          */
2920         'uploaded' : true
2921         
2922     });
2923 };
2924  
2925 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2926     
2927      
2928     errorTimeout : 3000,
2929      
2930     images : false,
2931    
2932     fileCollection : false,
2933     allowBlank : true,
2934     
2935     multiple : true,
2936     
2937     getAutoCreate : function()
2938     {
2939         var im = {
2940             tag: 'input',
2941             type : 'file',
2942             cls : 'd-none  roo-card-upload-selector' 
2943           
2944         };
2945         if (this.multiple) {
2946             im.multiple = 'multiple';
2947         }
2948         
2949         return  {
2950             cls :'div' ,
2951             cn : [
2952                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2953                 im
2954
2955             ]
2956         };
2957            
2958          
2959     },
2960      
2961    
2962     initEvents : function()
2963     {
2964         
2965         Roo.bootstrap.Button.prototype.initEvents.call(this);
2966         
2967         
2968         
2969         
2970         
2971         this.urlAPI = (window.createObjectURL && window) || 
2972                                 (window.URL && URL.revokeObjectURL && URL) || 
2973                                 (window.webkitURL && webkitURL);
2974                         
2975          
2976          
2977          
2978         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2979         
2980         this.selectorEl.on('change', this.onFileSelected, this);
2981          
2982          
2983        
2984     },
2985     
2986    
2987     onClick : function(e)
2988     {
2989         e.preventDefault();
2990         
2991         if ( this.fireEvent('beforeselect', this) === false) {
2992             return;
2993         }
2994          
2995         this.selectorEl.dom.click();
2996          
2997     },
2998     
2999     onFileSelected : function(e)
3000     {
3001         e.preventDefault();
3002         
3003         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3004             return;
3005         }
3006         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3007         this.selectorEl.dom.value  = '';// hopefully reset..
3008         
3009         this.fireEvent('uploaded', this,  files );
3010         
3011     },
3012     
3013        
3014    
3015     
3016     /**
3017      * addCard - add an Attachment to the uploader
3018      * @param data - the data about the image to upload
3019      *
3020      * {
3021           id : 123
3022           title : "Title of file",
3023           is_uploaded : false,
3024           src : "http://.....",
3025           srcfile : { the File upload object },
3026           mimetype : file.type,
3027           preview : false,
3028           is_deleted : 0
3029           .. any other data...
3030         }
3031      *
3032      * 
3033     */
3034      
3035     reset: function()
3036     {
3037          
3038          this.selectorEl
3039     } 
3040     
3041     
3042     
3043     
3044 });
3045  /*
3046  * - LGPL
3047  *
3048  * image
3049  * 
3050  */
3051
3052
3053 /**
3054  * @class Roo.bootstrap.Img
3055  * @extends Roo.bootstrap.Component
3056  * Bootstrap Img class
3057  * @cfg {Boolean} imgResponsive false | true
3058  * @cfg {String} border rounded | circle | thumbnail
3059  * @cfg {String} src image source
3060  * @cfg {String} alt image alternative text
3061  * @cfg {String} href a tag href
3062  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3063  * @cfg {String} xsUrl xs image source
3064  * @cfg {String} smUrl sm image source
3065  * @cfg {String} mdUrl md image source
3066  * @cfg {String} lgUrl lg image source
3067  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3068  * 
3069  * @constructor
3070  * Create a new Input
3071  * @param {Object} config The config object
3072  */
3073
3074 Roo.bootstrap.Img = function(config){
3075     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3076     
3077     this.addEvents({
3078         // img events
3079         /**
3080          * @event click
3081          * The img click event for the img.
3082          * @param {Roo.EventObject} e
3083          */
3084         "click" : true,
3085         /**
3086          * @event load
3087          * The when any image loads
3088          * @param {Roo.EventObject} e
3089          */
3090         "load" : true
3091     });
3092 };
3093
3094 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3095     
3096     imgResponsive: true,
3097     border: '',
3098     src: 'about:blank',
3099     href: false,
3100     target: false,
3101     xsUrl: '',
3102     smUrl: '',
3103     mdUrl: '',
3104     lgUrl: '',
3105     backgroundContain : false,
3106
3107     getAutoCreate : function()
3108     {   
3109         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3110             return this.createSingleImg();
3111         }
3112         
3113         var cfg = {
3114             tag: 'div',
3115             cls: 'roo-image-responsive-group',
3116             cn: []
3117         };
3118         var _this = this;
3119         
3120         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3121             
3122             if(!_this[size + 'Url']){
3123                 return;
3124             }
3125             
3126             var img = {
3127                 tag: 'img',
3128                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3129                 html: _this.html || cfg.html,
3130                 src: _this[size + 'Url']
3131             };
3132             
3133             img.cls += ' roo-image-responsive-' + size;
3134             
3135             var s = ['xs', 'sm', 'md', 'lg'];
3136             
3137             s.splice(s.indexOf(size), 1);
3138             
3139             Roo.each(s, function(ss){
3140                 img.cls += ' hidden-' + ss;
3141             });
3142             
3143             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3144                 cfg.cls += ' img-' + _this.border;
3145             }
3146             
3147             if(_this.alt){
3148                 cfg.alt = _this.alt;
3149             }
3150             
3151             if(_this.href){
3152                 var a = {
3153                     tag: 'a',
3154                     href: _this.href,
3155                     cn: [
3156                         img
3157                     ]
3158                 };
3159
3160                 if(this.target){
3161                     a.target = _this.target;
3162                 }
3163             }
3164             
3165             cfg.cn.push((_this.href) ? a : img);
3166             
3167         });
3168         
3169         return cfg;
3170     },
3171     
3172     createSingleImg : function()
3173     {
3174         var cfg = {
3175             tag: 'img',
3176             cls: (this.imgResponsive) ? 'img-responsive' : '',
3177             html : null,
3178             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3179         };
3180         
3181         if (this.backgroundContain) {
3182             cfg.cls += ' background-contain';
3183         }
3184         
3185         cfg.html = this.html || cfg.html;
3186         
3187         if (this.backgroundContain) {
3188             cfg.style="background-image: url(" + this.src + ')';
3189         } else {
3190             cfg.src = this.src || cfg.src;
3191         }
3192         
3193         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3194             cfg.cls += ' img-' + this.border;
3195         }
3196         
3197         if(this.alt){
3198             cfg.alt = this.alt;
3199         }
3200         
3201         if(this.href){
3202             var a = {
3203                 tag: 'a',
3204                 href: this.href,
3205                 cn: [
3206                     cfg
3207                 ]
3208             };
3209             
3210             if(this.target){
3211                 a.target = this.target;
3212             }
3213             
3214         }
3215         
3216         return (this.href) ? a : cfg;
3217     },
3218     
3219     initEvents: function() 
3220     {
3221         if(!this.href){
3222             this.el.on('click', this.onClick, this);
3223         }
3224         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3225             this.el.on('load', this.onImageLoad, this);
3226         } else {
3227             // not sure if this works.. not tested
3228             this.el.select('img', true).on('load', this.onImageLoad, this);
3229         }
3230         
3231     },
3232     
3233     onClick : function(e)
3234     {
3235         Roo.log('img onclick');
3236         this.fireEvent('click', this, e);
3237     },
3238     onImageLoad: function(e)
3239     {
3240         Roo.log('img load');
3241         this.fireEvent('load', this, e);
3242     },
3243     
3244     /**
3245      * Sets the url of the image - used to update it
3246      * @param {String} url the url of the image
3247      */
3248     
3249     setSrc : function(url)
3250     {
3251         this.src =  url;
3252         
3253         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3254             if (this.backgroundContain) {
3255                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3256             } else {
3257                 this.el.dom.src =  url;
3258             }
3259             return;
3260         }
3261         
3262         this.el.select('img', true).first().dom.src =  url;
3263     }
3264     
3265     
3266    
3267 });
3268
3269  /*
3270  * - LGPL
3271  *
3272  * image
3273  * 
3274  */
3275
3276
3277 /**
3278  * @class Roo.bootstrap.Link
3279  * @extends Roo.bootstrap.Component
3280  * @children Roo.bootstrap.Component
3281  * Bootstrap Link Class (eg. '<a href>')
3282  
3283  * @cfg {String} alt image alternative text
3284  * @cfg {String} href a tag href
3285  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3286  * @cfg {String} html the content of the link.
3287  * @cfg {String} anchor name for the anchor link
3288  * @cfg {String} fa - favicon
3289
3290  * @cfg {Boolean} preventDefault (true | false) default false
3291
3292  * 
3293  * @constructor
3294  * Create a new Input
3295  * @param {Object} config The config object
3296  */
3297
3298 Roo.bootstrap.Link = function(config){
3299     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3300     
3301     this.addEvents({
3302         // img events
3303         /**
3304          * @event click
3305          * The img click event for the img.
3306          * @param {Roo.EventObject} e
3307          */
3308         "click" : true
3309     });
3310 };
3311
3312 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3313     
3314     href: false,
3315     target: false,
3316     preventDefault: false,
3317     anchor : false,
3318     alt : false,
3319     fa: false,
3320
3321
3322     getAutoCreate : function()
3323     {
3324         var html = this.html || '';
3325         
3326         if (this.fa !== false) {
3327             html = '<i class="fa fa-' + this.fa + '"></i>';
3328         }
3329         var cfg = {
3330             tag: 'a'
3331         };
3332         // anchor's do not require html/href...
3333         if (this.anchor === false) {
3334             cfg.html = html;
3335             cfg.href = this.href || '#';
3336         } else {
3337             cfg.name = this.anchor;
3338             if (this.html !== false || this.fa !== false) {
3339                 cfg.html = html;
3340             }
3341             if (this.href !== false) {
3342                 cfg.href = this.href;
3343             }
3344         }
3345         
3346         if(this.alt !== false){
3347             cfg.alt = this.alt;
3348         }
3349         
3350         
3351         if(this.target !== false) {
3352             cfg.target = this.target;
3353         }
3354         
3355         return cfg;
3356     },
3357     
3358     initEvents: function() {
3359         
3360         if(!this.href || this.preventDefault){
3361             this.el.on('click', this.onClick, this);
3362         }
3363     },
3364     
3365     onClick : function(e)
3366     {
3367         if(this.preventDefault){
3368             e.preventDefault();
3369         }
3370         //Roo.log('img onclick');
3371         this.fireEvent('click', this, e);
3372     }
3373    
3374 });
3375
3376  /*
3377  * - LGPL
3378  *
3379  * header
3380  * 
3381  */
3382
3383 /**
3384  * @class Roo.bootstrap.Header
3385  * @extends Roo.bootstrap.Component
3386  * @children Roo.bootstrap.Component
3387  * Bootstrap Header class
3388  *
3389  * 
3390  * @cfg {String} html content of header
3391  * @cfg {Number} level (1|2|3|4|5|6) default 1
3392  * 
3393  * @constructor
3394  * Create a new Header
3395  * @param {Object} config The config object
3396  */
3397
3398
3399 Roo.bootstrap.Header  = function(config){
3400     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3401 };
3402
3403 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3404     
3405     //href : false,
3406     html : false,
3407     level : 1,
3408     
3409     
3410     
3411     getAutoCreate : function(){
3412         
3413         
3414         
3415         var cfg = {
3416             tag: 'h' + (1 *this.level),
3417             html: this.html || ''
3418         } ;
3419         
3420         return cfg;
3421     }
3422    
3423 });
3424
3425  
3426
3427  /**
3428  * @class Roo.bootstrap.MenuMgr
3429  * @licence LGPL
3430  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3431  * @singleton
3432  */
3433 Roo.bootstrap.menu.Manager = function(){
3434    var menus, active, groups = {}, attached = false, lastShow = new Date();
3435
3436    // private - called when first menu is created
3437    function init(){
3438        menus = {};
3439        active = new Roo.util.MixedCollection();
3440        Roo.get(document).addKeyListener(27, function(){
3441            if(active.length > 0){
3442                hideAll();
3443            }
3444        });
3445    }
3446
3447    // private
3448    function hideAll(){
3449        if(active && active.length > 0){
3450            var c = active.clone();
3451            c.each(function(m){
3452                m.hide();
3453            });
3454        }
3455    }
3456
3457    // private
3458    function onHide(m){
3459        active.remove(m);
3460        if(active.length < 1){
3461            Roo.get(document).un("mouseup", onMouseDown);
3462             
3463            attached = false;
3464        }
3465    }
3466
3467    // private
3468    function onShow(m){
3469        var last = active.last();
3470        lastShow = new Date();
3471        active.add(m);
3472        if(!attached){
3473           Roo.get(document).on("mouseup", onMouseDown);
3474            
3475            attached = true;
3476        }
3477        if(m.parentMenu){
3478           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3479           m.parentMenu.activeChild = m;
3480        }else if(last && last.isVisible()){
3481           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3482        }
3483    }
3484
3485    // private
3486    function onBeforeHide(m){
3487        if(m.activeChild){
3488            m.activeChild.hide();
3489        }
3490        if(m.autoHideTimer){
3491            clearTimeout(m.autoHideTimer);
3492            delete m.autoHideTimer;
3493        }
3494    }
3495
3496    // private
3497    function onBeforeShow(m){
3498        var pm = m.parentMenu;
3499        if(!pm && !m.allowOtherMenus){
3500            hideAll();
3501        }else if(pm && pm.activeChild && active != m){
3502            pm.activeChild.hide();
3503        }
3504    }
3505
3506    // private this should really trigger on mouseup..
3507    function onMouseDown(e){
3508         Roo.log("on Mouse Up");
3509         
3510         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3511             Roo.log("MenuManager hideAll");
3512             hideAll();
3513             e.stopEvent();
3514         }
3515         
3516         
3517    }
3518
3519    // private
3520    function onBeforeCheck(mi, state){
3521        if(state){
3522            var g = groups[mi.group];
3523            for(var i = 0, l = g.length; i < l; i++){
3524                if(g[i] != mi){
3525                    g[i].setChecked(false);
3526                }
3527            }
3528        }
3529    }
3530
3531    return {
3532
3533        /**
3534         * Hides all menus that are currently visible
3535         */
3536        hideAll : function(){
3537             hideAll();  
3538        },
3539
3540        // private
3541        register : function(menu){
3542            if(!menus){
3543                init();
3544            }
3545            menus[menu.id] = menu;
3546            menu.on("beforehide", onBeforeHide);
3547            menu.on("hide", onHide);
3548            menu.on("beforeshow", onBeforeShow);
3549            menu.on("show", onShow);
3550            var g = menu.group;
3551            if(g && menu.events["checkchange"]){
3552                if(!groups[g]){
3553                    groups[g] = [];
3554                }
3555                groups[g].push(menu);
3556                menu.on("checkchange", onCheck);
3557            }
3558        },
3559
3560         /**
3561          * Returns a {@link Roo.menu.Menu} object
3562          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3563          * be used to generate and return a new Menu instance.
3564          */
3565        get : function(menu){
3566            if(typeof menu == "string"){ // menu id
3567                return menus[menu];
3568            }else if(menu.events){  // menu instance
3569                return menu;
3570            }
3571            /*else if(typeof menu.length == 'number'){ // array of menu items?
3572                return new Roo.bootstrap.Menu({items:menu});
3573            }else{ // otherwise, must be a config
3574                return new Roo.bootstrap.Menu(menu);
3575            }
3576            */
3577            return false;
3578        },
3579
3580        // private
3581        unregister : function(menu){
3582            delete menus[menu.id];
3583            menu.un("beforehide", onBeforeHide);
3584            menu.un("hide", onHide);
3585            menu.un("beforeshow", onBeforeShow);
3586            menu.un("show", onShow);
3587            var g = menu.group;
3588            if(g && menu.events["checkchange"]){
3589                groups[g].remove(menu);
3590                menu.un("checkchange", onCheck);
3591            }
3592        },
3593
3594        // private
3595        registerCheckable : function(menuItem){
3596            var g = menuItem.group;
3597            if(g){
3598                if(!groups[g]){
3599                    groups[g] = [];
3600                }
3601                groups[g].push(menuItem);
3602                menuItem.on("beforecheckchange", onBeforeCheck);
3603            }
3604        },
3605
3606        // private
3607        unregisterCheckable : function(menuItem){
3608            var g = menuItem.group;
3609            if(g){
3610                groups[g].remove(menuItem);
3611                menuItem.un("beforecheckchange", onBeforeCheck);
3612            }
3613        }
3614    };
3615 }(); 
3616 /**
3617  * @class Roo.bootstrap.menu.Menu
3618  * @extends Roo.bootstrap.Component
3619  * @licence LGPL
3620  * @children Roo.bootstrap.menu.Item
3621  * @parent none
3622  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3623  * 
3624  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3625  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3626  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3627  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3628   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3629   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3630  
3631  * @constructor
3632  * Create a new Menu
3633  * @param {Object} config The config objectQ
3634  */
3635
3636
3637 Roo.bootstrap.menu.Menu = function(config){
3638     
3639     if (config.type == 'treeview') {
3640         // normally menu's are drawn attached to the document to handle layering etc..
3641         // however treeview (used by the docs menu is drawn into the parent element)
3642         this.container_method = 'getChildContainer'; 
3643     }
3644     
3645     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3646     if (this.registerMenu && this.type != 'treeview')  {
3647         Roo.bootstrap.menu.Manager.register(this);
3648     }
3649     
3650     
3651     this.addEvents({
3652         /**
3653          * @event beforeshow
3654          * Fires before this menu is displayed (return false to block)
3655          * @param {Roo.menu.Menu} this
3656          */
3657         beforeshow : true,
3658         /**
3659          * @event beforehide
3660          * Fires before this menu is hidden (return false to block)
3661          * @param {Roo.menu.Menu} this
3662          */
3663         beforehide : true,
3664         /**
3665          * @event show
3666          * Fires after this menu is displayed
3667          * @param {Roo.menu.Menu} this
3668          */
3669         show : true,
3670         /**
3671          * @event hide
3672          * Fires after this menu is hidden
3673          * @param {Roo.menu.Menu} this
3674          */
3675         hide : true,
3676         /**
3677          * @event click
3678          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3679          * @param {Roo.menu.Menu} this
3680          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3681          * @param {Roo.EventObject} e
3682          */
3683         click : true,
3684         /**
3685          * @event mouseover
3686          * Fires when the mouse is hovering over this menu
3687          * @param {Roo.menu.Menu} this
3688          * @param {Roo.EventObject} e
3689          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3690          */
3691         mouseover : true,
3692         /**
3693          * @event mouseout
3694          * Fires when the mouse exits this menu
3695          * @param {Roo.menu.Menu} this
3696          * @param {Roo.EventObject} e
3697          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3698          */
3699         mouseout : true,
3700         /**
3701          * @event itemclick
3702          * Fires when a menu item contained in this menu is clicked
3703          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3704          * @param {Roo.EventObject} e
3705          */
3706         itemclick: true
3707     });
3708     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3709 };
3710
3711 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3712     
3713    /// html : false,
3714    
3715     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3716     type: false,
3717     /**
3718      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3719      */
3720     registerMenu : true,
3721     
3722     menuItems :false, // stores the menu items..
3723     
3724     hidden:true,
3725         
3726     parentMenu : false,
3727     
3728     stopEvent : true,
3729     
3730     isLink : false,
3731     
3732     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3733     
3734     hideTrigger : false,
3735     
3736     align : 'tl-bl?',
3737     
3738     
3739     getChildContainer : function() {
3740         return this.el;  
3741     },
3742     
3743     getAutoCreate : function(){
3744          
3745         //if (['right'].indexOf(this.align)!==-1) {
3746         //    cfg.cn[1].cls += ' pull-right'
3747         //}
3748          
3749         var cfg = {
3750             tag : 'ul',
3751             cls : 'dropdown-menu shadow' ,
3752             style : 'z-index:1000'
3753             
3754         };
3755         
3756         if (this.type === 'submenu') {
3757             cfg.cls = 'submenu active';
3758         }
3759         if (this.type === 'treeview') {
3760             cfg.cls = 'treeview-menu';
3761         }
3762         
3763         return cfg;
3764     },
3765     initEvents : function() {
3766         
3767        // Roo.log("ADD event");
3768        // Roo.log(this.triggerEl.dom);
3769         if (this.triggerEl) {
3770             
3771             this.triggerEl.on('click', this.onTriggerClick, this);
3772             
3773             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3774             
3775             if (!this.hideTrigger) {
3776                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3777                     // dropdown toggle on the 'a' in BS4?
3778                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3779                 } else {
3780                     this.triggerEl.addClass('dropdown-toggle');
3781                 }
3782             }
3783         }
3784         
3785         if (Roo.isTouch) {
3786             this.el.on('touchstart'  , this.onTouch, this);
3787         }
3788         this.el.on('click' , this.onClick, this);
3789
3790         this.el.on("mouseover", this.onMouseOver, this);
3791         this.el.on("mouseout", this.onMouseOut, this);
3792         
3793     },
3794     
3795     findTargetItem : function(e)
3796     {
3797         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3798         if(!t){
3799             return false;
3800         }
3801         //Roo.log(t);         Roo.log(t.id);
3802         if(t && t.id){
3803             //Roo.log(this.menuitems);
3804             return this.menuitems.get(t.id);
3805             
3806             //return this.items.get(t.menuItemId);
3807         }
3808         
3809         return false;
3810     },
3811     
3812     onTouch : function(e) 
3813     {
3814         Roo.log("menu.onTouch");
3815         //e.stopEvent(); this make the user popdown broken
3816         this.onClick(e);
3817     },
3818     
3819     onClick : function(e)
3820     {
3821         Roo.log("menu.onClick");
3822         
3823         var t = this.findTargetItem(e);
3824         if(!t || t.isContainer){
3825             return;
3826         }
3827         Roo.log(e);
3828         /*
3829         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3830             if(t == this.activeItem && t.shouldDeactivate(e)){
3831                 this.activeItem.deactivate();
3832                 delete this.activeItem;
3833                 return;
3834             }
3835             if(t.canActivate){
3836                 this.setActiveItem(t, true);
3837             }
3838             return;
3839             
3840             
3841         }
3842         */
3843        
3844         Roo.log('pass click event');
3845         
3846         t.onClick(e);
3847         
3848         this.fireEvent("click", this, t, e);
3849         
3850         var _this = this;
3851         
3852         if(!t.href.length || t.href == '#'){
3853             (function() { _this.hide(); }).defer(100);
3854         }
3855         
3856     },
3857     
3858     onMouseOver : function(e){
3859         var t  = this.findTargetItem(e);
3860         //Roo.log(t);
3861         //if(t){
3862         //    if(t.canActivate && !t.disabled){
3863         //        this.setActiveItem(t, true);
3864         //    }
3865         //}
3866         
3867         this.fireEvent("mouseover", this, e, t);
3868     },
3869     isVisible : function(){
3870         return !this.hidden;
3871     },
3872     onMouseOut : function(e){
3873         var t  = this.findTargetItem(e);
3874         
3875         //if(t ){
3876         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3877         //        this.activeItem.deactivate();
3878         //        delete this.activeItem;
3879         //    }
3880         //}
3881         this.fireEvent("mouseout", this, e, t);
3882     },
3883     
3884     
3885     /**
3886      * Displays this menu relative to another element
3887      * @param {String/HTMLElement/Roo.Element} element The element to align to
3888      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3889      * the element (defaults to this.defaultAlign)
3890      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3891      */
3892     show : function(el, pos, parentMenu)
3893     {
3894         if (false === this.fireEvent("beforeshow", this)) {
3895             Roo.log("show canceled");
3896             return;
3897         }
3898         this.parentMenu = parentMenu;
3899         if(!this.el){
3900             this.render();
3901         }
3902         this.el.addClass('show'); // show otherwise we do not know how big we are..
3903          
3904         var xy = this.el.getAlignToXY(el, pos);
3905         
3906         // bl-tl << left align  below
3907         // tl-bl << left align 
3908         
3909         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3910             // if it goes to far to the right.. -> align left.
3911             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3912         }
3913         if(xy[0] < 0){
3914             // was left align - go right?
3915             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3916         }
3917         
3918         // goes down the bottom
3919         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3920            xy[1]  < 0 ){
3921             var a = this.align.replace('?', '').split('-');
3922             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3923             
3924         }
3925         
3926         this.showAt(  xy , parentMenu, false);
3927     },
3928      /**
3929      * Displays this menu at a specific xy position
3930      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3931      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3932      */
3933     showAt : function(xy, parentMenu, /* private: */_e){
3934         this.parentMenu = parentMenu;
3935         if(!this.el){
3936             this.render();
3937         }
3938         if(_e !== false){
3939             this.fireEvent("beforeshow", this);
3940             //xy = this.el.adjustForConstraints(xy);
3941         }
3942         
3943         //this.el.show();
3944         this.hideMenuItems();
3945         this.hidden = false;
3946         if (this.triggerEl) {
3947             this.triggerEl.addClass('open');
3948         }
3949         
3950         this.el.addClass('show');
3951         
3952         
3953         
3954         // reassign x when hitting right
3955         
3956         // reassign y when hitting bottom
3957         
3958         // but the list may align on trigger left or trigger top... should it be a properity?
3959         
3960         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3961             this.el.setXY(xy);
3962         }
3963         
3964         this.focus();
3965         this.fireEvent("show", this);
3966     },
3967     
3968     focus : function(){
3969         return;
3970         if(!this.hidden){
3971             this.doFocus.defer(50, this);
3972         }
3973     },
3974
3975     doFocus : function(){
3976         if(!this.hidden){
3977             this.focusEl.focus();
3978         }
3979     },
3980
3981     /**
3982      * Hides this menu and optionally all parent menus
3983      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3984      */
3985     hide : function(deep)
3986     {
3987         if (false === this.fireEvent("beforehide", this)) {
3988             Roo.log("hide canceled");
3989             return;
3990         }
3991         this.hideMenuItems();
3992         if(this.el && this.isVisible()){
3993            
3994             if(this.activeItem){
3995                 this.activeItem.deactivate();
3996                 this.activeItem = null;
3997             }
3998             if (this.triggerEl) {
3999                 this.triggerEl.removeClass('open');
4000             }
4001             
4002             this.el.removeClass('show');
4003             this.hidden = true;
4004             this.fireEvent("hide", this);
4005         }
4006         if(deep === true && this.parentMenu){
4007             this.parentMenu.hide(true);
4008         }
4009     },
4010     
4011     onTriggerClick : function(e)
4012     {
4013         Roo.log('trigger click');
4014         
4015         var target = e.getTarget();
4016         
4017         Roo.log(target.nodeName.toLowerCase());
4018         
4019         if(target.nodeName.toLowerCase() === 'i'){
4020             e.preventDefault();
4021         }
4022         
4023     },
4024     
4025     onTriggerPress  : function(e)
4026     {
4027         Roo.log('trigger press');
4028         //Roo.log(e.getTarget());
4029        // Roo.log(this.triggerEl.dom);
4030        
4031         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4032         var pel = Roo.get(e.getTarget());
4033         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4034             Roo.log('is treeview or dropdown?');
4035             return;
4036         }
4037         
4038         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4039             return;
4040         }
4041         
4042         if (this.isVisible()) {
4043             Roo.log('hide');
4044             this.hide();
4045         } else {
4046             Roo.log('show');
4047             
4048             this.show(this.triggerEl, this.align, false);
4049         }
4050         
4051         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4052             e.stopEvent();
4053         }
4054         
4055     },
4056        
4057     
4058     hideMenuItems : function()
4059     {
4060         Roo.log("hide Menu Items");
4061         if (!this.el) { 
4062             return;
4063         }
4064         
4065         this.el.select('.open',true).each(function(aa) {
4066             
4067             aa.removeClass('open');
4068          
4069         });
4070     },
4071     addxtypeChild : function (tree, cntr) {
4072         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4073           
4074         this.menuitems.add(comp);
4075         return comp;
4076
4077     },
4078     getEl : function()
4079     {
4080         Roo.log(this.el);
4081         return this.el;
4082     },
4083     
4084     clear : function()
4085     {
4086         this.getEl().dom.innerHTML = '';
4087         this.menuitems.clear();
4088     }
4089 });
4090
4091  
4092  /**
4093  * @class Roo.bootstrap.menu.Item
4094  * @extends Roo.bootstrap.Component
4095  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4096  * @parent Roo.bootstrap.menu.Menu
4097  * @licence LGPL
4098  * Bootstrap MenuItem class
4099  * 
4100  * @cfg {String} html the menu label
4101  * @cfg {String} href the link
4102  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4103  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4104  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4105  * @cfg {String} fa favicon to show on left of menu item.
4106  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4107  * 
4108  * 
4109  * @constructor
4110  * Create a new MenuItem
4111  * @param {Object} config The config object
4112  */
4113
4114
4115 Roo.bootstrap.menu.Item = function(config){
4116     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4117     this.addEvents({
4118         // raw events
4119         /**
4120          * @event click
4121          * The raw click event for the entire grid.
4122          * @param {Roo.bootstrap.menu.Item} this
4123          * @param {Roo.EventObject} e
4124          */
4125         "click" : true
4126     });
4127 };
4128
4129 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4130     
4131     href : false,
4132     html : false,
4133     preventDefault: false,
4134     isContainer : false,
4135     active : false,
4136     fa: false,
4137     
4138     getAutoCreate : function(){
4139         
4140         if(this.isContainer){
4141             return {
4142                 tag: 'li',
4143                 cls: 'dropdown-menu-item '
4144             };
4145         }
4146         var ctag = {
4147             tag: 'span',
4148             html: 'Link'
4149         };
4150         
4151         var anc = {
4152             tag : 'a',
4153             cls : 'dropdown-item',
4154             href : '#',
4155             cn : [  ]
4156         };
4157         
4158         if (this.fa !== false) {
4159             anc.cn.push({
4160                 tag : 'i',
4161                 cls : 'fa fa-' + this.fa
4162             });
4163         }
4164         
4165         anc.cn.push(ctag);
4166         
4167         
4168         var cfg= {
4169             tag: 'li',
4170             cls: 'dropdown-menu-item',
4171             cn: [ anc ]
4172         };
4173         if (this.parent().type == 'treeview') {
4174             cfg.cls = 'treeview-menu';
4175         }
4176         if (this.active) {
4177             cfg.cls += ' active';
4178         }
4179         
4180         
4181         
4182         anc.href = this.href || cfg.cn[0].href ;
4183         ctag.html = this.html || cfg.cn[0].html ;
4184         return cfg;
4185     },
4186     
4187     initEvents: function()
4188     {
4189         if (this.parent().type == 'treeview') {
4190             this.el.select('a').on('click', this.onClick, this);
4191         }
4192         
4193         if (this.menu) {
4194             this.menu.parentType = this.xtype;
4195             this.menu.triggerEl = this.el;
4196             this.menu = this.addxtype(Roo.apply({}, this.menu));
4197         }
4198         
4199     },
4200     onClick : function(e)
4201     {
4202         Roo.log('item on click ');
4203         
4204         if(this.preventDefault){
4205             e.preventDefault();
4206         }
4207         //this.parent().hideMenuItems();
4208         
4209         this.fireEvent('click', this, e);
4210     },
4211     getEl : function()
4212     {
4213         return this.el;
4214     } 
4215 });
4216
4217  
4218
4219  
4220
4221   
4222 /**
4223  * @class Roo.bootstrap.menu.Separator
4224  * @extends Roo.bootstrap.Component
4225  * @licence LGPL
4226  * @parent Roo.bootstrap.menu.Menu
4227  * Bootstrap Separator class
4228  * 
4229  * @constructor
4230  * Create a new Separator
4231  * @param {Object} config The config object
4232  */
4233
4234
4235 Roo.bootstrap.menu.Separator = function(config){
4236     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4237 };
4238
4239 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4240     
4241     getAutoCreate : function(){
4242         var cfg = {
4243             tag : 'li',
4244             cls: 'dropdown-divider divider'
4245         };
4246         
4247         return cfg;
4248     }
4249    
4250 });
4251
4252  
4253
4254  
4255 /*
4256 * Licence: LGPL
4257 */
4258
4259 /**
4260  * @class Roo.bootstrap.Modal
4261  * @extends Roo.bootstrap.Component
4262  * @builder-top
4263  * @parent none
4264  * @children Roo.bootstrap.Component
4265  * Bootstrap Modal class
4266  * @cfg {String} title Title of dialog
4267  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4268  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4269  * @cfg {Boolean} specificTitle default false
4270  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4271  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4272  * @cfg {Boolean} animate default true
4273  * @cfg {Boolean} allow_close default true
4274  * @cfg {Boolean} fitwindow default false
4275  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4276  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4277  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4278  * @cfg {String} size (sm|lg|xl) default empty
4279  * @cfg {Number} max_width set the max width of modal
4280  * @cfg {Boolean} editableTitle can the title be edited
4281
4282  *
4283  *
4284  * @constructor
4285  * Create a new Modal Dialog
4286  * @param {Object} config The config object
4287  */
4288
4289 Roo.bootstrap.Modal = function(config){
4290     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4291     this.addEvents({
4292         // raw events
4293         /**
4294          * @event btnclick
4295          * The raw btnclick event for the button
4296          * @param {Roo.EventObject} e
4297          */
4298         "btnclick" : true,
4299         /**
4300          * @event resize
4301          * Fire when dialog resize
4302          * @param {Roo.bootstrap.Modal} this
4303          * @param {Roo.EventObject} e
4304          */
4305         "resize" : true,
4306         /**
4307          * @event titlechanged
4308          * Fire when the editable title has been changed
4309          * @param {Roo.bootstrap.Modal} this
4310          * @param {Roo.EventObject} value
4311          */
4312         "titlechanged" : true 
4313         
4314     });
4315     this.buttons = this.buttons || [];
4316
4317     if (this.tmpl) {
4318         this.tmpl = Roo.factory(this.tmpl);
4319     }
4320
4321 };
4322
4323 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4324
4325     title : 'test dialog',
4326
4327     buttons : false,
4328
4329     // set on load...
4330
4331     html: false,
4332
4333     tmp: false,
4334
4335     specificTitle: false,
4336
4337     buttonPosition: 'right',
4338
4339     allow_close : true,
4340
4341     animate : true,
4342
4343     fitwindow: false,
4344     
4345      // private
4346     dialogEl: false,
4347     bodyEl:  false,
4348     footerEl:  false,
4349     titleEl:  false,
4350     closeEl:  false,
4351
4352     size: '',
4353     
4354     max_width: 0,
4355     
4356     max_height: 0,
4357     
4358     fit_content: false,
4359     editableTitle  : false,
4360
4361     onRender : function(ct, position)
4362     {
4363         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4364
4365         if(!this.el){
4366             var cfg = Roo.apply({},  this.getAutoCreate());
4367             cfg.id = Roo.id();
4368             //if(!cfg.name){
4369             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4370             //}
4371             //if (!cfg.name.length) {
4372             //    delete cfg.name;
4373            // }
4374             if (this.cls) {
4375                 cfg.cls += ' ' + this.cls;
4376             }
4377             if (this.style) {
4378                 cfg.style = this.style;
4379             }
4380             this.el = Roo.get(document.body).createChild(cfg, position);
4381         }
4382         //var type = this.el.dom.type;
4383
4384
4385         if(this.tabIndex !== undefined){
4386             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4387         }
4388
4389         this.dialogEl = this.el.select('.modal-dialog',true).first();
4390         this.bodyEl = this.el.select('.modal-body',true).first();
4391         this.closeEl = this.el.select('.modal-header .close', true).first();
4392         this.headerEl = this.el.select('.modal-header',true).first();
4393         this.titleEl = this.el.select('.modal-title',true).first();
4394         this.footerEl = this.el.select('.modal-footer',true).first();
4395
4396         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4397         
4398         //this.el.addClass("x-dlg-modal");
4399
4400         if (this.buttons.length) {
4401             Roo.each(this.buttons, function(bb) {
4402                 var b = Roo.apply({}, bb);
4403                 b.xns = b.xns || Roo.bootstrap;
4404                 b.xtype = b.xtype || 'Button';
4405                 if (typeof(b.listeners) == 'undefined') {
4406                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4407                 }
4408
4409                 var btn = Roo.factory(b);
4410
4411                 btn.render(this.getButtonContainer());
4412
4413             },this);
4414         }
4415         // render the children.
4416         var nitems = [];
4417
4418         if(typeof(this.items) != 'undefined'){
4419             var items = this.items;
4420             delete this.items;
4421
4422             for(var i =0;i < items.length;i++) {
4423                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4424             }
4425         }
4426
4427         this.items = nitems;
4428
4429         // where are these used - they used to be body/close/footer
4430
4431
4432         this.initEvents();
4433         //this.el.addClass([this.fieldClass, this.cls]);
4434
4435     },
4436
4437     getAutoCreate : function()
4438     {
4439         // we will default to modal-body-overflow - might need to remove or make optional later.
4440         var bdy = {
4441                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4442                 html : this.html || ''
4443         };
4444
4445         var title = {
4446             tag: 'h5',
4447             cls : 'modal-title',
4448             html : this.title
4449         };
4450
4451         if(this.specificTitle){ // WTF is this?
4452             title = this.title;
4453         }
4454
4455         var header = [];
4456         if (this.allow_close && Roo.bootstrap.version == 3) {
4457             header.push({
4458                 tag: 'button',
4459                 cls : 'close',
4460                 html : '&times'
4461             });
4462         }
4463
4464         header.push(title);
4465
4466         if (this.editableTitle) {
4467             header.push({
4468                 cls: 'form-control roo-editable-title d-none',
4469                 tag: 'input',
4470                 type: 'text'
4471             });
4472         }
4473         
4474         if (this.allow_close && Roo.bootstrap.version == 4) {
4475             header.push({
4476                 tag: 'button',
4477                 cls : 'close',
4478                 html : '&times'
4479             });
4480         }
4481         
4482         var size = '';
4483
4484         if(this.size.length){
4485             size = 'modal-' + this.size;
4486         }
4487         
4488         var footer = Roo.bootstrap.version == 3 ?
4489             {
4490                 cls : 'modal-footer',
4491                 cn : [
4492                     {
4493                         tag: 'div',
4494                         cls: 'btn-' + this.buttonPosition
4495                     }
4496                 ]
4497
4498             } :
4499             {  // BS4 uses mr-auto on left buttons....
4500                 cls : 'modal-footer'
4501             };
4502
4503             
4504
4505         
4506         
4507         var modal = {
4508             cls: "modal",
4509              cn : [
4510                 {
4511                     cls: "modal-dialog " + size,
4512                     cn : [
4513                         {
4514                             cls : "modal-content",
4515                             cn : [
4516                                 {
4517                                     cls : 'modal-header',
4518                                     cn : header
4519                                 },
4520                                 bdy,
4521                                 footer
4522                             ]
4523
4524                         }
4525                     ]
4526
4527                 }
4528             ]
4529         };
4530
4531         if(this.animate){
4532             modal.cls += ' fade';
4533         }
4534
4535         return modal;
4536
4537     },
4538     getChildContainer : function() {
4539
4540          return this.bodyEl;
4541
4542     },
4543     getButtonContainer : function() {
4544         
4545          return Roo.bootstrap.version == 4 ?
4546             this.el.select('.modal-footer',true).first()
4547             : this.el.select('.modal-footer div',true).first();
4548
4549     },
4550     initEvents : function()
4551     {
4552         if (this.allow_close) {
4553             this.closeEl.on('click', this.hide, this);
4554         }
4555         Roo.EventManager.onWindowResize(this.resize, this, true);
4556         if (this.editableTitle) {
4557             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4558             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4559             this.headerEditEl.on('keyup', function(e) {
4560                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4561                         this.toggleHeaderInput(false)
4562                     }
4563                 }, this);
4564             this.headerEditEl.on('blur', function(e) {
4565                 this.toggleHeaderInput(false)
4566             },this);
4567         }
4568
4569     },
4570   
4571
4572     resize : function()
4573     {
4574         this.maskEl.setSize(
4575             Roo.lib.Dom.getViewWidth(true),
4576             Roo.lib.Dom.getViewHeight(true)
4577         );
4578         
4579         if (this.fitwindow) {
4580             
4581            this.dialogEl.setStyle( { 'max-width' : '100%' });
4582             this.setSize(
4583                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4584                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4585             );
4586             return;
4587         }
4588         
4589         if(this.max_width !== 0) {
4590             
4591             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4592             
4593             if(this.height) {
4594                 this.setSize(w, this.height);
4595                 return;
4596             }
4597             
4598             if(this.max_height) {
4599                 this.setSize(w,Math.min(
4600                     this.max_height,
4601                     Roo.lib.Dom.getViewportHeight(true) - 60
4602                 ));
4603                 
4604                 return;
4605             }
4606             
4607             if(!this.fit_content) {
4608                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4609                 return;
4610             }
4611             
4612             this.setSize(w, Math.min(
4613                 60 +
4614                 this.headerEl.getHeight() + 
4615                 this.footerEl.getHeight() + 
4616                 this.getChildHeight(this.bodyEl.dom.childNodes),
4617                 Roo.lib.Dom.getViewportHeight(true) - 60)
4618             );
4619         }
4620         
4621     },
4622
4623     setSize : function(w,h)
4624     {
4625         if (!w && !h) {
4626             return;
4627         }
4628         
4629         this.resizeTo(w,h);
4630     },
4631
4632     show : function() {
4633
4634         if (!this.rendered) {
4635             this.render();
4636         }
4637         this.toggleHeaderInput(false);
4638         //this.el.setStyle('display', 'block');
4639         this.el.removeClass('hideing');
4640         this.el.dom.style.display='block';
4641         
4642         Roo.get(document.body).addClass('modal-open');
4643  
4644         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4645             
4646             (function(){
4647                 this.el.addClass('show');
4648                 this.el.addClass('in');
4649             }).defer(50, this);
4650         }else{
4651             this.el.addClass('show');
4652             this.el.addClass('in');
4653         }
4654
4655         // not sure how we can show data in here..
4656         //if (this.tmpl) {
4657         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4658         //}
4659
4660         Roo.get(document.body).addClass("x-body-masked");
4661         
4662         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4663         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4664         this.maskEl.dom.style.display = 'block';
4665         this.maskEl.addClass('show');
4666         
4667         
4668         this.resize();
4669         
4670         this.fireEvent('show', this);
4671
4672         // set zindex here - otherwise it appears to be ignored...
4673         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4674
4675         (function () {
4676             this.items.forEach( function(e) {
4677                 e.layout ? e.layout() : false;
4678
4679             });
4680         }).defer(100,this);
4681
4682     },
4683     hide : function()
4684     {
4685         if(this.fireEvent("beforehide", this) !== false){
4686             
4687             this.maskEl.removeClass('show');
4688             
4689             this.maskEl.dom.style.display = '';
4690             Roo.get(document.body).removeClass("x-body-masked");
4691             this.el.removeClass('in');
4692             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4693
4694             if(this.animate){ // why
4695                 this.el.addClass('hideing');
4696                 this.el.removeClass('show');
4697                 (function(){
4698                     if (!this.el.hasClass('hideing')) {
4699                         return; // it's been shown again...
4700                     }
4701                     
4702                     this.el.dom.style.display='';
4703
4704                     Roo.get(document.body).removeClass('modal-open');
4705                     this.el.removeClass('hideing');
4706                 }).defer(150,this);
4707                 
4708             }else{
4709                 this.el.removeClass('show');
4710                 this.el.dom.style.display='';
4711                 Roo.get(document.body).removeClass('modal-open');
4712
4713             }
4714             this.fireEvent('hide', this);
4715         }
4716     },
4717     isVisible : function()
4718     {
4719         
4720         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4721         
4722     },
4723
4724     addButton : function(str, cb)
4725     {
4726
4727
4728         var b = Roo.apply({}, { html : str } );
4729         b.xns = b.xns || Roo.bootstrap;
4730         b.xtype = b.xtype || 'Button';
4731         if (typeof(b.listeners) == 'undefined') {
4732             b.listeners = { click : cb.createDelegate(this)  };
4733         }
4734
4735         var btn = Roo.factory(b);
4736
4737         btn.render(this.getButtonContainer());
4738
4739         return btn;
4740
4741     },
4742
4743     setDefaultButton : function(btn)
4744     {
4745         //this.el.select('.modal-footer').()
4746     },
4747
4748     resizeTo: function(w,h)
4749     {
4750         this.dialogEl.setWidth(w);
4751         
4752         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4753
4754         this.bodyEl.setHeight(h - diff);
4755         
4756         this.fireEvent('resize', this);
4757     },
4758     
4759     setContentSize  : function(w, h)
4760     {
4761
4762     },
4763     onButtonClick: function(btn,e)
4764     {
4765         //Roo.log([a,b,c]);
4766         this.fireEvent('btnclick', btn.name, e);
4767     },
4768      /**
4769      * Set the title of the Dialog
4770      * @param {String} str new Title
4771      */
4772     setTitle: function(str) {
4773         this.titleEl.dom.innerHTML = str;
4774         this.title = str;
4775     },
4776     /**
4777      * Set the body of the Dialog
4778      * @param {String} str new Title
4779      */
4780     setBody: function(str) {
4781         this.bodyEl.dom.innerHTML = str;
4782     },
4783     /**
4784      * Set the body of the Dialog using the template
4785      * @param {Obj} data - apply this data to the template and replace the body contents.
4786      */
4787     applyBody: function(obj)
4788     {
4789         if (!this.tmpl) {
4790             Roo.log("Error - using apply Body without a template");
4791             //code
4792         }
4793         this.tmpl.overwrite(this.bodyEl, obj);
4794     },
4795     
4796     getChildHeight : function(child_nodes)
4797     {
4798         if(
4799             !child_nodes ||
4800             child_nodes.length == 0
4801         ) {
4802             return 0;
4803         }
4804         
4805         var child_height = 0;
4806         
4807         for(var i = 0; i < child_nodes.length; i++) {
4808             
4809             /*
4810             * for modal with tabs...
4811             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4812                 
4813                 var layout_childs = child_nodes[i].childNodes;
4814                 
4815                 for(var j = 0; j < layout_childs.length; j++) {
4816                     
4817                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4818                         
4819                         var layout_body_childs = layout_childs[j].childNodes;
4820                         
4821                         for(var k = 0; k < layout_body_childs.length; k++) {
4822                             
4823                             if(layout_body_childs[k].classList.contains('navbar')) {
4824                                 child_height += layout_body_childs[k].offsetHeight;
4825                                 continue;
4826                             }
4827                             
4828                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4829                                 
4830                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4831                                 
4832                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4833                                     
4834                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4835                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4836                                         continue;
4837                                     }
4838                                     
4839                                 }
4840                                 
4841                             }
4842                             
4843                         }
4844                     }
4845                 }
4846                 continue;
4847             }
4848             */
4849             
4850             child_height += child_nodes[i].offsetHeight;
4851             // Roo.log(child_nodes[i].offsetHeight);
4852         }
4853         
4854         return child_height;
4855     },
4856     toggleHeaderInput : function(is_edit)
4857     {
4858         if (!this.editableTitle) {
4859             return; // not editable.
4860         }
4861         if (is_edit && this.is_header_editing) {
4862             return; // already editing..
4863         }
4864         if (is_edit) {
4865     
4866             this.headerEditEl.dom.value = this.title;
4867             this.headerEditEl.removeClass('d-none');
4868             this.headerEditEl.dom.focus();
4869             this.titleEl.addClass('d-none');
4870             
4871             this.is_header_editing = true;
4872             return
4873         }
4874         // flip back to not editing.
4875         this.title = this.headerEditEl.dom.value;
4876         this.headerEditEl.addClass('d-none');
4877         this.titleEl.removeClass('d-none');
4878         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4879         this.is_header_editing = false;
4880         this.fireEvent('titlechanged', this, this.title);
4881     
4882             
4883         
4884     }
4885
4886 });
4887
4888
4889 Roo.apply(Roo.bootstrap.Modal,  {
4890     /**
4891          * Button config that displays a single OK button
4892          * @type Object
4893          */
4894         OK :  [{
4895             name : 'ok',
4896             weight : 'primary',
4897             html : 'OK'
4898         }],
4899         /**
4900          * Button config that displays Yes and No buttons
4901          * @type Object
4902          */
4903         YESNO : [
4904             {
4905                 name  : 'no',
4906                 html : 'No'
4907             },
4908             {
4909                 name  :'yes',
4910                 weight : 'primary',
4911                 html : 'Yes'
4912             }
4913         ],
4914
4915         /**
4916          * Button config that displays OK and Cancel buttons
4917          * @type Object
4918          */
4919         OKCANCEL : [
4920             {
4921                name : 'cancel',
4922                 html : 'Cancel'
4923             },
4924             {
4925                 name : 'ok',
4926                 weight : 'primary',
4927                 html : 'OK'
4928             }
4929         ],
4930         /**
4931          * Button config that displays Yes, No and Cancel buttons
4932          * @type Object
4933          */
4934         YESNOCANCEL : [
4935             {
4936                 name : 'yes',
4937                 weight : 'primary',
4938                 html : 'Yes'
4939             },
4940             {
4941                 name : 'no',
4942                 html : 'No'
4943             },
4944             {
4945                 name : 'cancel',
4946                 html : 'Cancel'
4947             }
4948         ],
4949         
4950         zIndex : 10001
4951 });
4952
4953 /*
4954  * - LGPL
4955  *
4956  * messagebox - can be used as a replace
4957  * 
4958  */
4959 /**
4960  * @class Roo.MessageBox
4961  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4962  * Example usage:
4963  *<pre><code>
4964 // Basic alert:
4965 Roo.Msg.alert('Status', 'Changes saved successfully.');
4966
4967 // Prompt for user data:
4968 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4969     if (btn == 'ok'){
4970         // process text value...
4971     }
4972 });
4973
4974 // Show a dialog using config options:
4975 Roo.Msg.show({
4976    title:'Save Changes?',
4977    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4978    buttons: Roo.Msg.YESNOCANCEL,
4979    fn: processResult,
4980    animEl: 'elId'
4981 });
4982 </code></pre>
4983  * @singleton
4984  */
4985 Roo.bootstrap.MessageBox = function(){
4986     var dlg, opt, mask, waitTimer;
4987     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4988     var buttons, activeTextEl, bwidth;
4989
4990     
4991     // private
4992     var handleButton = function(button){
4993         dlg.hide();
4994         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4995     };
4996
4997     // private
4998     var handleHide = function(){
4999         if(opt && opt.cls){
5000             dlg.el.removeClass(opt.cls);
5001         }
5002         //if(waitTimer){
5003         //    Roo.TaskMgr.stop(waitTimer);
5004         //    waitTimer = null;
5005         //}
5006     };
5007
5008     // private
5009     var updateButtons = function(b){
5010         var width = 0;
5011         if(!b){
5012             buttons["ok"].hide();
5013             buttons["cancel"].hide();
5014             buttons["yes"].hide();
5015             buttons["no"].hide();
5016             dlg.footerEl.hide();
5017             
5018             return width;
5019         }
5020         dlg.footerEl.show();
5021         for(var k in buttons){
5022             if(typeof buttons[k] != "function"){
5023                 if(b[k]){
5024                     buttons[k].show();
5025                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5026                     width += buttons[k].el.getWidth()+15;
5027                 }else{
5028                     buttons[k].hide();
5029                 }
5030             }
5031         }
5032         return width;
5033     };
5034
5035     // private
5036     var handleEsc = function(d, k, e){
5037         if(opt && opt.closable !== false){
5038             dlg.hide();
5039         }
5040         if(e){
5041             e.stopEvent();
5042         }
5043     };
5044
5045     return {
5046         /**
5047          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5048          * @return {Roo.BasicDialog} The BasicDialog element
5049          */
5050         getDialog : function(){
5051            if(!dlg){
5052                 dlg = new Roo.bootstrap.Modal( {
5053                     //draggable: true,
5054                     //resizable:false,
5055                     //constraintoviewport:false,
5056                     //fixedcenter:true,
5057                     //collapsible : false,
5058                     //shim:true,
5059                     //modal: true,
5060                 //    width: 'auto',
5061                   //  height:100,
5062                     //buttonAlign:"center",
5063                     closeClick : function(){
5064                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5065                             handleButton("no");
5066                         }else{
5067                             handleButton("cancel");
5068                         }
5069                     }
5070                 });
5071                 dlg.render();
5072                 dlg.on("hide", handleHide);
5073                 mask = dlg.mask;
5074                 //dlg.addKeyListener(27, handleEsc);
5075                 buttons = {};
5076                 this.buttons = buttons;
5077                 var bt = this.buttonText;
5078                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5079                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5080                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5081                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5082                 //Roo.log(buttons);
5083                 bodyEl = dlg.bodyEl.createChild({
5084
5085                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5086                         '<textarea class="roo-mb-textarea"></textarea>' +
5087                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5088                 });
5089                 msgEl = bodyEl.dom.firstChild;
5090                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5091                 textboxEl.enableDisplayMode();
5092                 textboxEl.addKeyListener([10,13], function(){
5093                     if(dlg.isVisible() && opt && opt.buttons){
5094                         if(opt.buttons.ok){
5095                             handleButton("ok");
5096                         }else if(opt.buttons.yes){
5097                             handleButton("yes");
5098                         }
5099                     }
5100                 });
5101                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5102                 textareaEl.enableDisplayMode();
5103                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5104                 progressEl.enableDisplayMode();
5105                 
5106                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5107                 var pf = progressEl.dom.firstChild;
5108                 if (pf) {
5109                     pp = Roo.get(pf.firstChild);
5110                     pp.setHeight(pf.offsetHeight);
5111                 }
5112                 
5113             }
5114             return dlg;
5115         },
5116
5117         /**
5118          * Updates the message box body text
5119          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5120          * the XHTML-compliant non-breaking space character '&amp;#160;')
5121          * @return {Roo.MessageBox} This message box
5122          */
5123         updateText : function(text)
5124         {
5125             if(!dlg.isVisible() && !opt.width){
5126                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5127                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5128             }
5129             msgEl.innerHTML = text || '&#160;';
5130       
5131             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5132             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5133             var w = Math.max(
5134                     Math.min(opt.width || cw , this.maxWidth), 
5135                     Math.max(opt.minWidth || this.minWidth, bwidth)
5136             );
5137             if(opt.prompt){
5138                 activeTextEl.setWidth(w);
5139             }
5140             if(dlg.isVisible()){
5141                 dlg.fixedcenter = false;
5142             }
5143             // to big, make it scroll. = But as usual stupid IE does not support
5144             // !important..
5145             
5146             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5147                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5148                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5149             } else {
5150                 bodyEl.dom.style.height = '';
5151                 bodyEl.dom.style.overflowY = '';
5152             }
5153             if (cw > w) {
5154                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5155             } else {
5156                 bodyEl.dom.style.overflowX = '';
5157             }
5158             
5159             dlg.setContentSize(w, bodyEl.getHeight());
5160             if(dlg.isVisible()){
5161                 dlg.fixedcenter = true;
5162             }
5163             return this;
5164         },
5165
5166         /**
5167          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5168          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5169          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5170          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5171          * @return {Roo.MessageBox} This message box
5172          */
5173         updateProgress : function(value, text){
5174             if(text){
5175                 this.updateText(text);
5176             }
5177             
5178             if (pp) { // weird bug on my firefox - for some reason this is not defined
5179                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5180                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5181             }
5182             return this;
5183         },        
5184
5185         /**
5186          * Returns true if the message box is currently displayed
5187          * @return {Boolean} True if the message box is visible, else false
5188          */
5189         isVisible : function(){
5190             return dlg && dlg.isVisible();  
5191         },
5192
5193         /**
5194          * Hides the message box if it is displayed
5195          */
5196         hide : function(){
5197             if(this.isVisible()){
5198                 dlg.hide();
5199             }  
5200         },
5201
5202         /**
5203          * Displays a new message box, or reinitializes an existing message box, based on the config options
5204          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5205          * The following config object properties are supported:
5206          * <pre>
5207 Property    Type             Description
5208 ----------  ---------------  ------------------------------------------------------------------------------------
5209 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5210                                    closes (defaults to undefined)
5211 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5212                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5213 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5214                                    progress and wait dialogs will ignore this property and always hide the
5215                                    close button as they can only be closed programmatically.
5216 cls               String           A custom CSS class to apply to the message box element
5217 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5218                                    displayed (defaults to 75)
5219 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5220                                    function will be btn (the name of the button that was clicked, if applicable,
5221                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5222                                    Progress and wait dialogs will ignore this option since they do not respond to
5223                                    user actions and can only be closed programmatically, so any required function
5224                                    should be called by the same code after it closes the dialog.
5225 icon              String           A CSS class that provides a background image to be used as an icon for
5226                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5227 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5228 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5229 modal             Boolean          False to allow user interaction with the page while the message box is
5230                                    displayed (defaults to true)
5231 msg               String           A string that will replace the existing message box body text (defaults
5232                                    to the XHTML-compliant non-breaking space character '&#160;')
5233 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5234 progress          Boolean          True to display a progress bar (defaults to false)
5235 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5236 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5237 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5238 title             String           The title text
5239 value             String           The string value to set into the active textbox element if displayed
5240 wait              Boolean          True to display a progress bar (defaults to false)
5241 width             Number           The width of the dialog in pixels
5242 </pre>
5243          *
5244          * Example usage:
5245          * <pre><code>
5246 Roo.Msg.show({
5247    title: 'Address',
5248    msg: 'Please enter your address:',
5249    width: 300,
5250    buttons: Roo.MessageBox.OKCANCEL,
5251    multiline: true,
5252    fn: saveAddress,
5253    animEl: 'addAddressBtn'
5254 });
5255 </code></pre>
5256          * @param {Object} config Configuration options
5257          * @return {Roo.MessageBox} This message box
5258          */
5259         show : function(options)
5260         {
5261             
5262             // this causes nightmares if you show one dialog after another
5263             // especially on callbacks..
5264              
5265             if(this.isVisible()){
5266                 
5267                 this.hide();
5268                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5269                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5270                 Roo.log("New Dialog Message:" +  options.msg )
5271                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5272                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5273                 
5274             }
5275             var d = this.getDialog();
5276             opt = options;
5277             d.setTitle(opt.title || "&#160;");
5278             d.closeEl.setDisplayed(opt.closable !== false);
5279             activeTextEl = textboxEl;
5280             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5281             if(opt.prompt){
5282                 if(opt.multiline){
5283                     textboxEl.hide();
5284                     textareaEl.show();
5285                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5286                         opt.multiline : this.defaultTextHeight);
5287                     activeTextEl = textareaEl;
5288                 }else{
5289                     textboxEl.show();
5290                     textareaEl.hide();
5291                 }
5292             }else{
5293                 textboxEl.hide();
5294                 textareaEl.hide();
5295             }
5296             progressEl.setDisplayed(opt.progress === true);
5297             if (opt.progress) {
5298                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5299             }
5300             this.updateProgress(0);
5301             activeTextEl.dom.value = opt.value || "";
5302             if(opt.prompt){
5303                 dlg.setDefaultButton(activeTextEl);
5304             }else{
5305                 var bs = opt.buttons;
5306                 var db = null;
5307                 if(bs && bs.ok){
5308                     db = buttons["ok"];
5309                 }else if(bs && bs.yes){
5310                     db = buttons["yes"];
5311                 }
5312                 dlg.setDefaultButton(db);
5313             }
5314             bwidth = updateButtons(opt.buttons);
5315             this.updateText(opt.msg);
5316             if(opt.cls){
5317                 d.el.addClass(opt.cls);
5318             }
5319             d.proxyDrag = opt.proxyDrag === true;
5320             d.modal = opt.modal !== false;
5321             d.mask = opt.modal !== false ? mask : false;
5322             if(!d.isVisible()){
5323                 // force it to the end of the z-index stack so it gets a cursor in FF
5324                 document.body.appendChild(dlg.el.dom);
5325                 d.animateTarget = null;
5326                 d.show(options.animEl);
5327             }
5328             return this;
5329         },
5330
5331         /**
5332          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5333          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5334          * and closing the message box when the process is complete.
5335          * @param {String} title The title bar text
5336          * @param {String} msg The message box body text
5337          * @return {Roo.MessageBox} This message box
5338          */
5339         progress : function(title, msg){
5340             this.show({
5341                 title : title,
5342                 msg : msg,
5343                 buttons: false,
5344                 progress:true,
5345                 closable:false,
5346                 minWidth: this.minProgressWidth,
5347                 modal : true
5348             });
5349             return this;
5350         },
5351
5352         /**
5353          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5354          * If a callback function is passed it will be called after the user clicks the button, and the
5355          * id of the button that was clicked will be passed as the only parameter to the callback
5356          * (could also be the top-right close button).
5357          * @param {String} title The title bar text
5358          * @param {String} msg The message box body text
5359          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5360          * @param {Object} scope (optional) The scope of the callback function
5361          * @return {Roo.MessageBox} This message box
5362          */
5363         alert : function(title, msg, fn, scope)
5364         {
5365             this.show({
5366                 title : title,
5367                 msg : msg,
5368                 buttons: this.OK,
5369                 fn: fn,
5370                 closable : false,
5371                 scope : scope,
5372                 modal : true
5373             });
5374             return this;
5375         },
5376
5377         /**
5378          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5379          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5380          * You are responsible for closing the message box when the process is complete.
5381          * @param {String} msg The message box body text
5382          * @param {String} title (optional) The title bar text
5383          * @return {Roo.MessageBox} This message box
5384          */
5385         wait : function(msg, title){
5386             this.show({
5387                 title : title,
5388                 msg : msg,
5389                 buttons: false,
5390                 closable:false,
5391                 progress:true,
5392                 modal:true,
5393                 width:300,
5394                 wait:true
5395             });
5396             waitTimer = Roo.TaskMgr.start({
5397                 run: function(i){
5398                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5399                 },
5400                 interval: 1000
5401             });
5402             return this;
5403         },
5404
5405         /**
5406          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5407          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5408          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5409          * @param {String} title The title bar text
5410          * @param {String} msg The message box body text
5411          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5412          * @param {Object} scope (optional) The scope of the callback function
5413          * @return {Roo.MessageBox} This message box
5414          */
5415         confirm : function(title, msg, fn, scope){
5416             this.show({
5417                 title : title,
5418                 msg : msg,
5419                 buttons: this.YESNO,
5420                 fn: fn,
5421                 scope : scope,
5422                 modal : true
5423             });
5424             return this;
5425         },
5426
5427         /**
5428          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5429          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5430          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5431          * (could also be the top-right close button) and the text that was entered will be passed as the two
5432          * parameters to the callback.
5433          * @param {String} title The title bar text
5434          * @param {String} msg The message box body text
5435          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5436          * @param {Object} scope (optional) The scope of the callback function
5437          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5438          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5439          * @return {Roo.MessageBox} This message box
5440          */
5441         prompt : function(title, msg, fn, scope, multiline){
5442             this.show({
5443                 title : title,
5444                 msg : msg,
5445                 buttons: this.OKCANCEL,
5446                 fn: fn,
5447                 minWidth:250,
5448                 scope : scope,
5449                 prompt:true,
5450                 multiline: multiline,
5451                 modal : true
5452             });
5453             return this;
5454         },
5455
5456         /**
5457          * Button config that displays a single OK button
5458          * @type Object
5459          */
5460         OK : {ok:true},
5461         /**
5462          * Button config that displays Yes and No buttons
5463          * @type Object
5464          */
5465         YESNO : {yes:true, no:true},
5466         /**
5467          * Button config that displays OK and Cancel buttons
5468          * @type Object
5469          */
5470         OKCANCEL : {ok:true, cancel:true},
5471         /**
5472          * Button config that displays Yes, No and Cancel buttons
5473          * @type Object
5474          */
5475         YESNOCANCEL : {yes:true, no:true, cancel:true},
5476
5477         /**
5478          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5479          * @type Number
5480          */
5481         defaultTextHeight : 75,
5482         /**
5483          * The maximum width in pixels of the message box (defaults to 600)
5484          * @type Number
5485          */
5486         maxWidth : 600,
5487         /**
5488          * The minimum width in pixels of the message box (defaults to 100)
5489          * @type Number
5490          */
5491         minWidth : 100,
5492         /**
5493          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5494          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5495          * @type Number
5496          */
5497         minProgressWidth : 250,
5498         /**
5499          * An object containing the default button text strings that can be overriden for localized language support.
5500          * Supported properties are: ok, cancel, yes and no.
5501          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5502          * @type Object
5503          */
5504         buttonText : {
5505             ok : "OK",
5506             cancel : "Cancel",
5507             yes : "Yes",
5508             no : "No"
5509         }
5510     };
5511 }();
5512
5513 /**
5514  * Shorthand for {@link Roo.MessageBox}
5515  */
5516 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5517 Roo.Msg = Roo.Msg || Roo.MessageBox;
5518 /*
5519  * - LGPL
5520  *
5521  * navbar
5522  * 
5523  */
5524
5525 /**
5526  * @class Roo.bootstrap.nav.Bar
5527  * @extends Roo.bootstrap.Component
5528  * @abstract
5529  * Bootstrap Navbar class
5530
5531  * @constructor
5532  * Create a new Navbar
5533  * @param {Object} config The config object
5534  */
5535
5536
5537 Roo.bootstrap.nav.Bar = function(config){
5538     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5539     this.addEvents({
5540         // raw events
5541         /**
5542          * @event beforetoggle
5543          * Fire before toggle the menu
5544          * @param {Roo.EventObject} e
5545          */
5546         "beforetoggle" : true
5547     });
5548 };
5549
5550 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5551     
5552     
5553    
5554     // private
5555     navItems : false,
5556     loadMask : false,
5557     
5558     
5559     getAutoCreate : function(){
5560         
5561         
5562         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5563         
5564     },
5565     
5566     initEvents :function ()
5567     {
5568         //Roo.log(this.el.select('.navbar-toggle',true));
5569         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5570         
5571         var mark = {
5572             tag: "div",
5573             cls:"x-dlg-mask"
5574         };
5575         
5576         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5577         
5578         var size = this.el.getSize();
5579         this.maskEl.setSize(size.width, size.height);
5580         this.maskEl.enableDisplayMode("block");
5581         this.maskEl.hide();
5582         
5583         if(this.loadMask){
5584             this.maskEl.show();
5585         }
5586     },
5587     
5588     
5589     getChildContainer : function()
5590     {
5591         if (this.el && this.el.select('.collapse').getCount()) {
5592             return this.el.select('.collapse',true).first();
5593         }
5594         
5595         return this.el;
5596     },
5597     
5598     mask : function()
5599     {
5600         this.maskEl.show();
5601     },
5602     
5603     unmask : function()
5604     {
5605         this.maskEl.hide();
5606     },
5607     onToggle : function()
5608     {
5609         
5610         if(this.fireEvent('beforetoggle', this) === false){
5611             return;
5612         }
5613         var ce = this.el.select('.navbar-collapse',true).first();
5614       
5615         if (!ce.hasClass('show')) {
5616            this.expand();
5617         } else {
5618             this.collapse();
5619         }
5620         
5621         
5622     
5623     },
5624     /**
5625      * Expand the navbar pulldown 
5626      */
5627     expand : function ()
5628     {
5629        
5630         var ce = this.el.select('.navbar-collapse',true).first();
5631         if (ce.hasClass('collapsing')) {
5632             return;
5633         }
5634         ce.dom.style.height = '';
5635                // show it...
5636         ce.addClass('in'); // old...
5637         ce.removeClass('collapse');
5638         ce.addClass('show');
5639         var h = ce.getHeight();
5640         Roo.log(h);
5641         ce.removeClass('show');
5642         // at this point we should be able to see it..
5643         ce.addClass('collapsing');
5644         
5645         ce.setHeight(0); // resize it ...
5646         ce.on('transitionend', function() {
5647             //Roo.log('done transition');
5648             ce.removeClass('collapsing');
5649             ce.addClass('show');
5650             ce.removeClass('collapse');
5651
5652             ce.dom.style.height = '';
5653         }, this, { single: true} );
5654         ce.setHeight(h);
5655         ce.dom.scrollTop = 0;
5656     },
5657     /**
5658      * Collapse the navbar pulldown 
5659      */
5660     collapse : function()
5661     {
5662          var ce = this.el.select('.navbar-collapse',true).first();
5663        
5664         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5665             // it's collapsed or collapsing..
5666             return;
5667         }
5668         ce.removeClass('in'); // old...
5669         ce.setHeight(ce.getHeight());
5670         ce.removeClass('show');
5671         ce.addClass('collapsing');
5672         
5673         ce.on('transitionend', function() {
5674             ce.dom.style.height = '';
5675             ce.removeClass('collapsing');
5676             ce.addClass('collapse');
5677         }, this, { single: true} );
5678         ce.setHeight(0);
5679     }
5680     
5681     
5682     
5683 });
5684
5685
5686
5687  
5688
5689  /*
5690  * - LGPL
5691  *
5692  * navbar
5693  * 
5694  */
5695
5696 /**
5697  * @class Roo.bootstrap.nav.Simplebar
5698  * @extends Roo.bootstrap.nav.Bar
5699  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5700  * Bootstrap Sidebar class
5701  *
5702  * @cfg {Boolean} inverse is inverted color
5703  * 
5704  * @cfg {String} type (nav | pills | tabs)
5705  * @cfg {Boolean} arrangement stacked | justified
5706  * @cfg {String} align (left | right) alignment
5707  * 
5708  * @cfg {Boolean} main (true|false) main nav bar? default false
5709  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5710  * 
5711  * @cfg {String} tag (header|footer|nav|div) default is nav 
5712
5713  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5714  * 
5715  * 
5716  * @constructor
5717  * Create a new Sidebar
5718  * @param {Object} config The config object
5719  */
5720
5721
5722 Roo.bootstrap.nav.Simplebar = function(config){
5723     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5724 };
5725
5726 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5727     
5728     inverse: false,
5729     
5730     type: false,
5731     arrangement: '',
5732     align : false,
5733     
5734     weight : 'light',
5735     
5736     main : false,
5737     
5738     
5739     tag : false,
5740     
5741     
5742     getAutoCreate : function(){
5743         
5744         
5745         var cfg = {
5746             tag : this.tag || 'div',
5747             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5748         };
5749         if (['light','white'].indexOf(this.weight) > -1) {
5750             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5751         }
5752         cfg.cls += ' bg-' + this.weight;
5753         
5754         if (this.inverse) {
5755             cfg.cls += ' navbar-inverse';
5756             
5757         }
5758         
5759         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5760         
5761         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5762             return cfg;
5763         }
5764         
5765         
5766     
5767         
5768         cfg.cn = [
5769             {
5770                 cls: 'nav nav-' + this.xtype,
5771                 tag : 'ul'
5772             }
5773         ];
5774         
5775          
5776         this.type = this.type || 'nav';
5777         if (['tabs','pills'].indexOf(this.type) != -1) {
5778             cfg.cn[0].cls += ' nav-' + this.type
5779         
5780         
5781         } else {
5782             if (this.type!=='nav') {
5783                 Roo.log('nav type must be nav/tabs/pills')
5784             }
5785             cfg.cn[0].cls += ' navbar-nav'
5786         }
5787         
5788         
5789         
5790         
5791         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5792             cfg.cn[0].cls += ' nav-' + this.arrangement;
5793         }
5794         
5795         
5796         if (this.align === 'right') {
5797             cfg.cn[0].cls += ' navbar-right';
5798         }
5799         
5800         
5801         
5802         
5803         return cfg;
5804     
5805         
5806     }
5807     
5808     
5809     
5810 });
5811
5812
5813
5814  
5815
5816  
5817        /*
5818  * - LGPL
5819  *
5820  * navbar
5821  * navbar-fixed-top
5822  * navbar-expand-md  fixed-top 
5823  */
5824
5825 /**
5826  * @class Roo.bootstrap.nav.Headerbar
5827  * @extends Roo.bootstrap.nav.Simplebar
5828  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5829  * Bootstrap Sidebar class
5830  *
5831  * @cfg {String} brand what is brand
5832  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5833  * @cfg {String} brand_href href of the brand
5834  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5835  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5836  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5837  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5838  * 
5839  * @constructor
5840  * Create a new Sidebar
5841  * @param {Object} config The config object
5842  */
5843
5844
5845 Roo.bootstrap.nav.Headerbar = function(config){
5846     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5847       
5848 };
5849
5850 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5851     
5852     position: '',
5853     brand: '',
5854     brand_href: false,
5855     srButton : true,
5856     autohide : false,
5857     desktopCenter : false,
5858    
5859     
5860     getAutoCreate : function(){
5861         
5862         var   cfg = {
5863             tag: this.nav || 'nav',
5864             cls: 'navbar navbar-expand-md',
5865             role: 'navigation',
5866             cn: []
5867         };
5868         
5869         var cn = cfg.cn;
5870         if (this.desktopCenter) {
5871             cn.push({cls : 'container', cn : []});
5872             cn = cn[0].cn;
5873         }
5874         
5875         if(this.srButton){
5876             var btn = {
5877                 tag: 'button',
5878                 type: 'button',
5879                 cls: 'navbar-toggle navbar-toggler',
5880                 'data-toggle': 'collapse',
5881                 cn: [
5882                     {
5883                         tag: 'span',
5884                         cls: 'sr-only',
5885                         html: 'Toggle navigation'
5886                     },
5887                     {
5888                         tag: 'span',
5889                         cls: 'icon-bar navbar-toggler-icon'
5890                     },
5891                     {
5892                         tag: 'span',
5893                         cls: 'icon-bar'
5894                     },
5895                     {
5896                         tag: 'span',
5897                         cls: 'icon-bar'
5898                     }
5899                 ]
5900             };
5901             
5902             cn.push( Roo.bootstrap.version == 4 ? btn : {
5903                 tag: 'div',
5904                 cls: 'navbar-header',
5905                 cn: [
5906                     btn
5907                 ]
5908             });
5909         }
5910         
5911         cn.push({
5912             tag: 'div',
5913             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5914             cn : []
5915         });
5916         
5917         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5918         
5919         if (['light','white'].indexOf(this.weight) > -1) {
5920             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5921         }
5922         cfg.cls += ' bg-' + this.weight;
5923         
5924         
5925         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5926             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5927             
5928             // tag can override this..
5929             
5930             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5931         }
5932         
5933         if (this.brand !== '') {
5934             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5935             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5936                 tag: 'a',
5937                 href: this.brand_href ? this.brand_href : '#',
5938                 cls: 'navbar-brand',
5939                 cn: [
5940                 this.brand
5941                 ]
5942             });
5943         }
5944         
5945         if(this.main){
5946             cfg.cls += ' main-nav';
5947         }
5948         
5949         
5950         return cfg;
5951
5952         
5953     },
5954     getHeaderChildContainer : function()
5955     {
5956         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5957             return this.el.select('.navbar-header',true).first();
5958         }
5959         
5960         return this.getChildContainer();
5961     },
5962     
5963     getChildContainer : function()
5964     {
5965          
5966         return this.el.select('.roo-navbar-collapse',true).first();
5967          
5968         
5969     },
5970     
5971     initEvents : function()
5972     {
5973         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5974         
5975         if (this.autohide) {
5976             
5977             var prevScroll = 0;
5978             var ft = this.el;
5979             
5980             Roo.get(document).on('scroll',function(e) {
5981                 var ns = Roo.get(document).getScroll().top;
5982                 var os = prevScroll;
5983                 prevScroll = ns;
5984                 
5985                 if(ns > os){
5986                     ft.removeClass('slideDown');
5987                     ft.addClass('slideUp');
5988                     return;
5989                 }
5990                 ft.removeClass('slideUp');
5991                 ft.addClass('slideDown');
5992                  
5993               
5994           },this);
5995         }
5996     }    
5997     
5998 });
5999
6000
6001
6002  
6003
6004  /*
6005  * - LGPL
6006  *
6007  * navbar
6008  * 
6009  */
6010
6011 /**
6012  * @class Roo.bootstrap.nav.Sidebar
6013  * @extends Roo.bootstrap.nav.Bar
6014  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6015  * Bootstrap Sidebar class
6016  * 
6017  * @constructor
6018  * Create a new Sidebar
6019  * @param {Object} config The config object
6020  */
6021
6022
6023 Roo.bootstrap.nav.Sidebar = function(config){
6024     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6025 };
6026
6027 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6028     
6029     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6030     
6031     getAutoCreate : function(){
6032         
6033         
6034         return  {
6035             tag: 'div',
6036             cls: 'sidebar sidebar-nav'
6037         };
6038     
6039         
6040     }
6041     
6042     
6043     
6044 });
6045
6046
6047
6048  
6049
6050  /*
6051  * - LGPL
6052  *
6053  * nav group
6054  * 
6055  */
6056
6057 /**
6058  * @class Roo.bootstrap.nav.Group
6059  * @extends Roo.bootstrap.Component
6060  * @children Roo.bootstrap.nav.Item
6061  * Bootstrap NavGroup class
6062  * @cfg {String} align (left|right)
6063  * @cfg {Boolean} inverse
6064  * @cfg {String} type (nav|pills|tab) default nav
6065  * @cfg {String} navId - reference Id for navbar.
6066  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6067  * 
6068  * @constructor
6069  * Create a new nav group
6070  * @param {Object} config The config object
6071  */
6072
6073 Roo.bootstrap.nav.Group = function(config){
6074     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6075     this.navItems = [];
6076    
6077     Roo.bootstrap.nav.Group.register(this);
6078      this.addEvents({
6079         /**
6080              * @event changed
6081              * Fires when the active item changes
6082              * @param {Roo.bootstrap.nav.Group} this
6083              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6084              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6085          */
6086         'changed': true
6087      });
6088     
6089 };
6090
6091 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6092     
6093     align: '',
6094     inverse: false,
6095     form: false,
6096     type: 'nav',
6097     navId : '',
6098     // private
6099     pilltype : true,
6100     
6101     navItems : false, 
6102     
6103     getAutoCreate : function()
6104     {
6105         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6106         
6107         cfg = {
6108             tag : 'ul',
6109             cls: 'nav' 
6110         };
6111         if (Roo.bootstrap.version == 4) {
6112             if (['tabs','pills'].indexOf(this.type) != -1) {
6113                 cfg.cls += ' nav-' + this.type; 
6114             } else {
6115                 // trying to remove so header bar can right align top?
6116                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6117                     // do not use on header bar... 
6118                     cfg.cls += ' navbar-nav';
6119                 }
6120             }
6121             
6122         } else {
6123             if (['tabs','pills'].indexOf(this.type) != -1) {
6124                 cfg.cls += ' nav-' + this.type
6125             } else {
6126                 if (this.type !== 'nav') {
6127                     Roo.log('nav type must be nav/tabs/pills')
6128                 }
6129                 cfg.cls += ' navbar-nav'
6130             }
6131         }
6132         
6133         if (this.parent() && this.parent().sidebar) {
6134             cfg = {
6135                 tag: 'ul',
6136                 cls: 'dashboard-menu sidebar-menu'
6137             };
6138             
6139             return cfg;
6140         }
6141         
6142         if (this.form === true) {
6143             cfg = {
6144                 tag: 'form',
6145                 cls: 'navbar-form form-inline'
6146             };
6147             //nav navbar-right ml-md-auto
6148             if (this.align === 'right') {
6149                 cfg.cls += ' navbar-right ml-md-auto';
6150             } else {
6151                 cfg.cls += ' navbar-left';
6152             }
6153         }
6154         
6155         if (this.align === 'right') {
6156             cfg.cls += ' navbar-right ml-md-auto';
6157         } else {
6158             cfg.cls += ' mr-auto';
6159         }
6160         
6161         if (this.inverse) {
6162             cfg.cls += ' navbar-inverse';
6163             
6164         }
6165         
6166         
6167         return cfg;
6168     },
6169     /**
6170     * sets the active Navigation item
6171     * @param {Roo.bootstrap.nav.Item} the new current navitem
6172     */
6173     setActiveItem : function(item)
6174     {
6175         var prev = false;
6176         Roo.each(this.navItems, function(v){
6177             if (v == item) {
6178                 return ;
6179             }
6180             if (v.isActive()) {
6181                 v.setActive(false, true);
6182                 prev = v;
6183                 
6184             }
6185             
6186         });
6187
6188         item.setActive(true, true);
6189         this.fireEvent('changed', this, item, prev);
6190         
6191         
6192     },
6193     /**
6194     * gets the active Navigation item
6195     * @return {Roo.bootstrap.nav.Item} the current navitem
6196     */
6197     getActive : function()
6198     {
6199         
6200         var prev = false;
6201         Roo.each(this.navItems, function(v){
6202             
6203             if (v.isActive()) {
6204                 prev = v;
6205                 
6206             }
6207             
6208         });
6209         return prev;
6210     },
6211     
6212     indexOfNav : function()
6213     {
6214         
6215         var prev = false;
6216         Roo.each(this.navItems, function(v,i){
6217             
6218             if (v.isActive()) {
6219                 prev = i;
6220                 
6221             }
6222             
6223         });
6224         return prev;
6225     },
6226     /**
6227     * adds a Navigation item
6228     * @param {Roo.bootstrap.nav.Item} the navitem to add
6229     */
6230     addItem : function(cfg)
6231     {
6232         if (this.form && Roo.bootstrap.version == 4) {
6233             cfg.tag = 'div';
6234         }
6235         var cn = new Roo.bootstrap.nav.Item(cfg);
6236         this.register(cn);
6237         cn.parentId = this.id;
6238         cn.onRender(this.el, null);
6239         return cn;
6240     },
6241     /**
6242     * register a Navigation item
6243     * @param {Roo.bootstrap.nav.Item} the navitem to add
6244     */
6245     register : function(item)
6246     {
6247         this.navItems.push( item);
6248         item.navId = this.navId;
6249     
6250     },
6251     
6252     /**
6253     * clear all the Navigation item
6254     */
6255    
6256     clearAll : function()
6257     {
6258         this.navItems = [];
6259         this.el.dom.innerHTML = '';
6260     },
6261     
6262     getNavItem: function(tabId)
6263     {
6264         var ret = false;
6265         Roo.each(this.navItems, function(e) {
6266             if (e.tabId == tabId) {
6267                ret =  e;
6268                return false;
6269             }
6270             return true;
6271             
6272         });
6273         return ret;
6274     },
6275     
6276     setActiveNext : function()
6277     {
6278         var i = this.indexOfNav(this.getActive());
6279         if (i > this.navItems.length) {
6280             return;
6281         }
6282         this.setActiveItem(this.navItems[i+1]);
6283     },
6284     setActivePrev : function()
6285     {
6286         var i = this.indexOfNav(this.getActive());
6287         if (i  < 1) {
6288             return;
6289         }
6290         this.setActiveItem(this.navItems[i-1]);
6291     },
6292     clearWasActive : function(except) {
6293         Roo.each(this.navItems, function(e) {
6294             if (e.tabId != except.tabId && e.was_active) {
6295                e.was_active = false;
6296                return false;
6297             }
6298             return true;
6299             
6300         });
6301     },
6302     getWasActive : function ()
6303     {
6304         var r = false;
6305         Roo.each(this.navItems, function(e) {
6306             if (e.was_active) {
6307                r = e;
6308                return false;
6309             }
6310             return true;
6311             
6312         });
6313         return r;
6314     }
6315     
6316     
6317 });
6318
6319  
6320 Roo.apply(Roo.bootstrap.nav.Group, {
6321     
6322     groups: {},
6323      /**
6324     * register a Navigation Group
6325     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6326     */
6327     register : function(navgrp)
6328     {
6329         this.groups[navgrp.navId] = navgrp;
6330         
6331     },
6332     /**
6333     * fetch a Navigation Group based on the navigation ID
6334     * @param {string} the navgroup to add
6335     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6336     */
6337     get: function(navId) {
6338         if (typeof(this.groups[navId]) == 'undefined') {
6339             return false;
6340             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6341         }
6342         return this.groups[navId] ;
6343     }
6344     
6345     
6346     
6347 });
6348
6349  /**
6350  * @class Roo.bootstrap.nav.Item
6351  * @extends Roo.bootstrap.Component
6352  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6353  * @parent Roo.bootstrap.nav.Group
6354  * @licence LGPL
6355  * Bootstrap Navbar.NavItem class
6356  * 
6357  * @cfg {String} href  link to
6358  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6359  * @cfg {Boolean} button_outline show and outlined button
6360  * @cfg {String} html content of button
6361  * @cfg {String} badge text inside badge
6362  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6363  * @cfg {String} glyphicon DEPRICATED - use fa
6364  * @cfg {String} icon DEPRICATED - use fa
6365  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6366  * @cfg {Boolean} active Is item active
6367  * @cfg {Boolean} disabled Is item disabled
6368  * @cfg {String} linkcls  Link Class
6369  * @cfg {Boolean} preventDefault (true | false) default false
6370  * @cfg {String} tabId the tab that this item activates.
6371  * @cfg {String} tagtype (a|span) render as a href or span?
6372  * @cfg {Boolean} animateRef (true|false) link to element default false  
6373  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6374   
6375  * @constructor
6376  * Create a new Navbar Item
6377  * @param {Object} config The config object
6378  */
6379 Roo.bootstrap.nav.Item = function(config){
6380     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6381     this.addEvents({
6382         // raw events
6383         /**
6384          * @event click
6385          * The raw click event for the entire grid.
6386          * @param {Roo.EventObject} e
6387          */
6388         "click" : true,
6389          /**
6390             * @event changed
6391             * Fires when the active item active state changes
6392             * @param {Roo.bootstrap.nav.Item} this
6393             * @param {boolean} state the new state
6394              
6395          */
6396         'changed': true,
6397         /**
6398             * @event scrollto
6399             * Fires when scroll to element
6400             * @param {Roo.bootstrap.nav.Item} this
6401             * @param {Object} options
6402             * @param {Roo.EventObject} e
6403              
6404          */
6405         'scrollto': true
6406     });
6407    
6408 };
6409
6410 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6411     
6412     href: false,
6413     html: '',
6414     badge: '',
6415     icon: false,
6416     fa : false,
6417     glyphicon: false,
6418     active: false,
6419     preventDefault : false,
6420     tabId : false,
6421     tagtype : 'a',
6422     tag: 'li',
6423     disabled : false,
6424     animateRef : false,
6425     was_active : false,
6426     button_weight : '',
6427     button_outline : false,
6428     linkcls : '',
6429     navLink: false,
6430     
6431     getAutoCreate : function(){
6432          
6433         var cfg = {
6434             tag: this.tag,
6435             cls: 'nav-item'
6436         };
6437         
6438         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6439         
6440         if (this.active) {
6441             cfg.cls +=  ' active' ;
6442         }
6443         if (this.disabled) {
6444             cfg.cls += ' disabled';
6445         }
6446         
6447         // BS4 only?
6448         if (this.button_weight.length) {
6449             cfg.tag = this.href ? 'a' : 'button';
6450             cfg.html = this.html || '';
6451             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6452             if (this.href) {
6453                 cfg.href = this.href;
6454             }
6455             if (this.fa) {
6456                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6457             } else {
6458                 cfg.cls += " nav-html";
6459             }
6460             
6461             // menu .. should add dropdown-menu class - so no need for carat..
6462             
6463             if (this.badge !== '') {
6464                  
6465                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6466             }
6467             return cfg;
6468         }
6469         
6470         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6471             cfg.cn = [
6472                 {
6473                     tag: this.tagtype,
6474                     href : this.href || "#",
6475                     html: this.html || '',
6476                     cls : ''
6477                 }
6478             ];
6479             if (this.tagtype == 'a') {
6480                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6481         
6482             }
6483             if (this.icon) {
6484                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6485             } else  if (this.fa) {
6486                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6487             } else if(this.glyphicon) {
6488                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6489             } else {
6490                 cfg.cn[0].cls += " nav-html";
6491             }
6492             
6493             if (this.menu) {
6494                 cfg.cn[0].html += " <span class='caret'></span>";
6495              
6496             }
6497             
6498             if (this.badge !== '') {
6499                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6500             }
6501         }
6502         
6503         
6504         
6505         return cfg;
6506     },
6507     onRender : function(ct, position)
6508     {
6509        // Roo.log("Call onRender: " + this.xtype);
6510         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6511             this.tag = 'div';
6512         }
6513         
6514         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6515         this.navLink = this.el.select('.nav-link',true).first();
6516         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6517         return ret;
6518     },
6519       
6520     
6521     initEvents: function() 
6522     {
6523         if (typeof (this.menu) != 'undefined') {
6524             this.menu.parentType = this.xtype;
6525             this.menu.triggerEl = this.el;
6526             this.menu = this.addxtype(Roo.apply({}, this.menu));
6527         }
6528         
6529         this.el.on('click', this.onClick, this);
6530         
6531         //if(this.tagtype == 'span'){
6532         //    this.el.select('span',true).on('click', this.onClick, this);
6533         //}
6534        
6535         // at this point parent should be available..
6536         this.parent().register(this);
6537     },
6538     
6539     onClick : function(e)
6540     {
6541         if (e.getTarget('.dropdown-menu-item')) {
6542             // did you click on a menu itemm.... - then don't trigger onclick..
6543             return;
6544         }
6545         
6546         if(
6547                 this.preventDefault || 
6548                 this.href == '#' 
6549         ){
6550             Roo.log("NavItem - prevent Default?");
6551             e.preventDefault();
6552         }
6553         
6554         if (this.disabled) {
6555             return;
6556         }
6557         
6558         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6559         if (tg && tg.transition) {
6560             Roo.log("waiting for the transitionend");
6561             return;
6562         }
6563         
6564         
6565         
6566         //Roo.log("fire event clicked");
6567         if(this.fireEvent('click', this, e) === false){
6568             return;
6569         };
6570         
6571         if(this.tagtype == 'span'){
6572             return;
6573         }
6574         
6575         //Roo.log(this.href);
6576         var ael = this.el.select('a',true).first();
6577         //Roo.log(ael);
6578         
6579         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6580             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6581             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6582                 return; // ignore... - it's a 'hash' to another page.
6583             }
6584             Roo.log("NavItem - prevent Default?");
6585             e.preventDefault();
6586             this.scrollToElement(e);
6587         }
6588         
6589         
6590         var p =  this.parent();
6591    
6592         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6593             if (typeof(p.setActiveItem) !== 'undefined') {
6594                 p.setActiveItem(this);
6595             }
6596         }
6597         
6598         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6599         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6600             // remove the collapsed menu expand...
6601             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6602         }
6603     },
6604     
6605     isActive: function () {
6606         return this.active
6607     },
6608     setActive : function(state, fire, is_was_active)
6609     {
6610         if (this.active && !state && this.navId) {
6611             this.was_active = true;
6612             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6613             if (nv) {
6614                 nv.clearWasActive(this);
6615             }
6616             
6617         }
6618         this.active = state;
6619         
6620         if (!state ) {
6621             this.el.removeClass('active');
6622             this.navLink ? this.navLink.removeClass('active') : false;
6623         } else if (!this.el.hasClass('active')) {
6624             
6625             this.el.addClass('active');
6626             if (Roo.bootstrap.version == 4 && this.navLink ) {
6627                 this.navLink.addClass('active');
6628             }
6629             
6630         }
6631         if (fire) {
6632             this.fireEvent('changed', this, state);
6633         }
6634         
6635         // show a panel if it's registered and related..
6636         
6637         if (!this.navId || !this.tabId || !state || is_was_active) {
6638             return;
6639         }
6640         
6641         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6642         if (!tg) {
6643             return;
6644         }
6645         var pan = tg.getPanelByName(this.tabId);
6646         if (!pan) {
6647             return;
6648         }
6649         // if we can not flip to new panel - go back to old nav highlight..
6650         if (false == tg.showPanel(pan)) {
6651             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6652             if (nv) {
6653                 var onav = nv.getWasActive();
6654                 if (onav) {
6655                     onav.setActive(true, false, true);
6656                 }
6657             }
6658             
6659         }
6660         
6661         
6662         
6663     },
6664      // this should not be here...
6665     setDisabled : function(state)
6666     {
6667         this.disabled = state;
6668         if (!state ) {
6669             this.el.removeClass('disabled');
6670         } else if (!this.el.hasClass('disabled')) {
6671             this.el.addClass('disabled');
6672         }
6673         
6674     },
6675     
6676     /**
6677      * Fetch the element to display the tooltip on.
6678      * @return {Roo.Element} defaults to this.el
6679      */
6680     tooltipEl : function()
6681     {
6682         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6683     },
6684     
6685     scrollToElement : function(e)
6686     {
6687         var c = document.body;
6688         
6689         /*
6690          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6691          */
6692         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6693             c = document.documentElement;
6694         }
6695         
6696         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6697         
6698         if(!target){
6699             return;
6700         }
6701
6702         var o = target.calcOffsetsTo(c);
6703         
6704         var options = {
6705             target : target,
6706             value : o[1]
6707         };
6708         
6709         this.fireEvent('scrollto', this, options, e);
6710         
6711         Roo.get(c).scrollTo('top', options.value, true);
6712         
6713         return;
6714     },
6715     /**
6716      * Set the HTML (text content) of the item
6717      * @param {string} html  content for the nav item
6718      */
6719     setHtml : function(html)
6720     {
6721         this.html = html;
6722         this.htmlEl.dom.innerHTML = html;
6723         
6724     } 
6725 });
6726  
6727
6728  /*
6729  * - LGPL
6730  *
6731  * sidebar item
6732  *
6733  *  li
6734  *    <span> icon </span>
6735  *    <span> text </span>
6736  *    <span>badge </span>
6737  */
6738
6739 /**
6740  * @class Roo.bootstrap.nav.SidebarItem
6741  * @extends Roo.bootstrap.nav.Item
6742  * Bootstrap Navbar.NavSidebarItem class
6743  * 
6744  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6745  * {Boolean} open is the menu open
6746  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6747  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6748  * {String} buttonSize (sm|md|lg)the extra classes for the button
6749  * {Boolean} showArrow show arrow next to the text (default true)
6750  * @constructor
6751  * Create a new Navbar Button
6752  * @param {Object} config The config object
6753  */
6754 Roo.bootstrap.nav.SidebarItem = function(config){
6755     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6756     this.addEvents({
6757         // raw events
6758         /**
6759          * @event click
6760          * The raw click event for the entire grid.
6761          * @param {Roo.EventObject} e
6762          */
6763         "click" : true,
6764          /**
6765             * @event changed
6766             * Fires when the active item active state changes
6767             * @param {Roo.bootstrap.nav.SidebarItem} this
6768             * @param {boolean} state the new state
6769              
6770          */
6771         'changed': true
6772     });
6773    
6774 };
6775
6776 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6777     
6778     badgeWeight : 'default',
6779     
6780     open: false,
6781     
6782     buttonView : false,
6783     
6784     buttonWeight : 'default',
6785     
6786     buttonSize : 'md',
6787     
6788     showArrow : true,
6789     
6790     getAutoCreate : function(){
6791         
6792         
6793         var a = {
6794                 tag: 'a',
6795                 href : this.href || '#',
6796                 cls: '',
6797                 html : '',
6798                 cn : []
6799         };
6800         
6801         if(this.buttonView){
6802             a = {
6803                 tag: 'button',
6804                 href : this.href || '#',
6805                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6806                 html : this.html,
6807                 cn : []
6808             };
6809         }
6810         
6811         var cfg = {
6812             tag: 'li',
6813             cls: '',
6814             cn: [ a ]
6815         };
6816         
6817         if (this.active) {
6818             cfg.cls += ' active';
6819         }
6820         
6821         if (this.disabled) {
6822             cfg.cls += ' disabled';
6823         }
6824         if (this.open) {
6825             cfg.cls += ' open x-open';
6826         }
6827         // left icon..
6828         if (this.glyphicon || this.icon) {
6829             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6830             a.cn.push({ tag : 'i', cls : c }) ;
6831         }
6832         
6833         if(!this.buttonView){
6834             var span = {
6835                 tag: 'span',
6836                 html : this.html || ''
6837             };
6838
6839             a.cn.push(span);
6840             
6841         }
6842         
6843         if (this.badge !== '') {
6844             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6845         }
6846         
6847         if (this.menu) {
6848             
6849             if(this.showArrow){
6850                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6851             }
6852             
6853             a.cls += ' dropdown-toggle treeview' ;
6854         }
6855         
6856         return cfg;
6857     },
6858     
6859     initEvents : function()
6860     { 
6861         if (typeof (this.menu) != 'undefined') {
6862             this.menu.parentType = this.xtype;
6863             this.menu.triggerEl = this.el;
6864             this.menu = this.addxtype(Roo.apply({}, this.menu));
6865         }
6866         
6867         this.el.on('click', this.onClick, this);
6868         
6869         if(this.badge !== ''){
6870             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6871         }
6872         
6873     },
6874     
6875     onClick : function(e)
6876     {
6877         if(this.disabled){
6878             e.preventDefault();
6879             return;
6880         }
6881         
6882         if(this.preventDefault){
6883             e.preventDefault();
6884         }
6885         
6886         this.fireEvent('click', this, e);
6887     },
6888     
6889     disable : function()
6890     {
6891         this.setDisabled(true);
6892     },
6893     
6894     enable : function()
6895     {
6896         this.setDisabled(false);
6897     },
6898     
6899     setDisabled : function(state)
6900     {
6901         if(this.disabled == state){
6902             return;
6903         }
6904         
6905         this.disabled = state;
6906         
6907         if (state) {
6908             this.el.addClass('disabled');
6909             return;
6910         }
6911         
6912         this.el.removeClass('disabled');
6913         
6914         return;
6915     },
6916     
6917     setActive : function(state)
6918     {
6919         if(this.active == state){
6920             return;
6921         }
6922         
6923         this.active = state;
6924         
6925         if (state) {
6926             this.el.addClass('active');
6927             return;
6928         }
6929         
6930         this.el.removeClass('active');
6931         
6932         return;
6933     },
6934     
6935     isActive: function () 
6936     {
6937         return this.active;
6938     },
6939     
6940     setBadge : function(str)
6941     {
6942         if(!this.badgeEl){
6943             return;
6944         }
6945         
6946         this.badgeEl.dom.innerHTML = str;
6947     }
6948     
6949    
6950      
6951  
6952 });
6953  
6954
6955  /*
6956  * - LGPL
6957  *
6958  * nav progress bar
6959  * 
6960  */
6961
6962 /**
6963  * @class Roo.bootstrap.nav.ProgressBar
6964  * @extends Roo.bootstrap.Component
6965  * @children Roo.bootstrap.nav.ProgressBarItem
6966  * Bootstrap NavProgressBar class
6967  * 
6968  * @constructor
6969  * Create a new nav progress bar - a bar indicating step along a process
6970  * @param {Object} config The config object
6971  */
6972
6973 Roo.bootstrap.nav.ProgressBar = function(config){
6974     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6975
6976     this.bullets = this.bullets || [];
6977    
6978 //    Roo.bootstrap.nav.ProgressBar.register(this);
6979      this.addEvents({
6980         /**
6981              * @event changed
6982              * Fires when the active item changes
6983              * @param {Roo.bootstrap.nav.ProgressBar} this
6984              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6985              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6986          */
6987         'changed': true
6988      });
6989     
6990 };
6991
6992 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
6993     /**
6994      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
6995      * Bullets for the Nav Progress bar for the toolbar
6996      */
6997     bullets : [],
6998     barItems : [],
6999     
7000     getAutoCreate : function()
7001     {
7002         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7003         
7004         cfg = {
7005             tag : 'div',
7006             cls : 'roo-navigation-bar-group',
7007             cn : [
7008                 {
7009                     tag : 'div',
7010                     cls : 'roo-navigation-top-bar'
7011                 },
7012                 {
7013                     tag : 'div',
7014                     cls : 'roo-navigation-bullets-bar',
7015                     cn : [
7016                         {
7017                             tag : 'ul',
7018                             cls : 'roo-navigation-bar'
7019                         }
7020                     ]
7021                 },
7022                 
7023                 {
7024                     tag : 'div',
7025                     cls : 'roo-navigation-bottom-bar'
7026                 }
7027             ]
7028             
7029         };
7030         
7031         return cfg;
7032         
7033     },
7034     
7035     initEvents: function() 
7036     {
7037         
7038     },
7039     
7040     onRender : function(ct, position) 
7041     {
7042         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7043         
7044         if(this.bullets.length){
7045             Roo.each(this.bullets, function(b){
7046                this.addItem(b);
7047             }, this);
7048         }
7049         
7050         this.format();
7051         
7052     },
7053     
7054     addItem : function(cfg)
7055     {
7056         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7057         
7058         item.parentId = this.id;
7059         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7060         
7061         if(cfg.html){
7062             var top = new Roo.bootstrap.Element({
7063                 tag : 'div',
7064                 cls : 'roo-navigation-bar-text'
7065             });
7066             
7067             var bottom = new Roo.bootstrap.Element({
7068                 tag : 'div',
7069                 cls : 'roo-navigation-bar-text'
7070             });
7071             
7072             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7073             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7074             
7075             var topText = new Roo.bootstrap.Element({
7076                 tag : 'span',
7077                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7078             });
7079             
7080             var bottomText = new Roo.bootstrap.Element({
7081                 tag : 'span',
7082                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7083             });
7084             
7085             topText.onRender(top.el, null);
7086             bottomText.onRender(bottom.el, null);
7087             
7088             item.topEl = top;
7089             item.bottomEl = bottom;
7090         }
7091         
7092         this.barItems.push(item);
7093         
7094         return item;
7095     },
7096     
7097     getActive : function()
7098     {
7099         var active = false;
7100         
7101         Roo.each(this.barItems, function(v){
7102             
7103             if (!v.isActive()) {
7104                 return;
7105             }
7106             
7107             active = v;
7108             return false;
7109             
7110         });
7111         
7112         return active;
7113     },
7114     
7115     setActiveItem : function(item)
7116     {
7117         var prev = false;
7118         
7119         Roo.each(this.barItems, function(v){
7120             if (v.rid == item.rid) {
7121                 return ;
7122             }
7123             
7124             if (v.isActive()) {
7125                 v.setActive(false);
7126                 prev = v;
7127             }
7128         });
7129
7130         item.setActive(true);
7131         
7132         this.fireEvent('changed', this, item, prev);
7133     },
7134     
7135     getBarItem: function(rid)
7136     {
7137         var ret = false;
7138         
7139         Roo.each(this.barItems, function(e) {
7140             if (e.rid != rid) {
7141                 return;
7142             }
7143             
7144             ret =  e;
7145             return false;
7146         });
7147         
7148         return ret;
7149     },
7150     
7151     indexOfItem : function(item)
7152     {
7153         var index = false;
7154         
7155         Roo.each(this.barItems, function(v, i){
7156             
7157             if (v.rid != item.rid) {
7158                 return;
7159             }
7160             
7161             index = i;
7162             return false
7163         });
7164         
7165         return index;
7166     },
7167     
7168     setActiveNext : function()
7169     {
7170         var i = this.indexOfItem(this.getActive());
7171         
7172         if (i > this.barItems.length) {
7173             return;
7174         }
7175         
7176         this.setActiveItem(this.barItems[i+1]);
7177     },
7178     
7179     setActivePrev : function()
7180     {
7181         var i = this.indexOfItem(this.getActive());
7182         
7183         if (i  < 1) {
7184             return;
7185         }
7186         
7187         this.setActiveItem(this.barItems[i-1]);
7188     },
7189     
7190     format : function()
7191     {
7192         if(!this.barItems.length){
7193             return;
7194         }
7195      
7196         var width = 100 / this.barItems.length;
7197         
7198         Roo.each(this.barItems, function(i){
7199             i.el.setStyle('width', width + '%');
7200             i.topEl.el.setStyle('width', width + '%');
7201             i.bottomEl.el.setStyle('width', width + '%');
7202         }, this);
7203         
7204     }
7205     
7206 });
7207 /*
7208  * - LGPL
7209  *
7210  * Nav Progress Item
7211  * 
7212  */
7213
7214 /**
7215  * @class Roo.bootstrap.nav.ProgressBarItem
7216  * @extends Roo.bootstrap.Component
7217  * Bootstrap NavProgressBarItem class
7218  * @cfg {String} rid the reference id
7219  * @cfg {Boolean} active (true|false) Is item active default false
7220  * @cfg {Boolean} disabled (true|false) Is item active default false
7221  * @cfg {String} html
7222  * @cfg {String} position (top|bottom) text position default bottom
7223  * @cfg {String} icon show icon instead of number
7224  * 
7225  * @constructor
7226  * Create a new NavProgressBarItem
7227  * @param {Object} config The config object
7228  */
7229 Roo.bootstrap.nav.ProgressBarItem = function(config){
7230     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7231     this.addEvents({
7232         // raw events
7233         /**
7234          * @event click
7235          * The raw click event for the entire grid.
7236          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7237          * @param {Roo.EventObject} e
7238          */
7239         "click" : true
7240     });
7241    
7242 };
7243
7244 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7245     
7246     rid : '',
7247     active : false,
7248     disabled : false,
7249     html : '',
7250     position : 'bottom',
7251     icon : false,
7252     
7253     getAutoCreate : function()
7254     {
7255         var iconCls = 'roo-navigation-bar-item-icon';
7256         
7257         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7258         
7259         var cfg = {
7260             tag: 'li',
7261             cls: 'roo-navigation-bar-item',
7262             cn : [
7263                 {
7264                     tag : 'i',
7265                     cls : iconCls
7266                 }
7267             ]
7268         };
7269         
7270         if(this.active){
7271             cfg.cls += ' active';
7272         }
7273         if(this.disabled){
7274             cfg.cls += ' disabled';
7275         }
7276         
7277         return cfg;
7278     },
7279     
7280     disable : function()
7281     {
7282         this.setDisabled(true);
7283     },
7284     
7285     enable : function()
7286     {
7287         this.setDisabled(false);
7288     },
7289     
7290     initEvents: function() 
7291     {
7292         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7293         
7294         this.iconEl.on('click', this.onClick, this);
7295     },
7296     
7297     onClick : function(e)
7298     {
7299         e.preventDefault();
7300         
7301         if(this.disabled){
7302             return;
7303         }
7304         
7305         if(this.fireEvent('click', this, e) === false){
7306             return;
7307         };
7308         
7309         this.parent().setActiveItem(this);
7310     },
7311     
7312     isActive: function () 
7313     {
7314         return this.active;
7315     },
7316     
7317     setActive : function(state)
7318     {
7319         if(this.active == state){
7320             return;
7321         }
7322         
7323         this.active = state;
7324         
7325         if (state) {
7326             this.el.addClass('active');
7327             return;
7328         }
7329         
7330         this.el.removeClass('active');
7331         
7332         return;
7333     },
7334     
7335     setDisabled : function(state)
7336     {
7337         if(this.disabled == state){
7338             return;
7339         }
7340         
7341         this.disabled = state;
7342         
7343         if (state) {
7344             this.el.addClass('disabled');
7345             return;
7346         }
7347         
7348         this.el.removeClass('disabled');
7349     },
7350     
7351     tooltipEl : function()
7352     {
7353         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7354     }
7355 });
7356  
7357
7358  /*
7359  * - LGPL
7360  *
7361  *  Breadcrumb Nav
7362  * 
7363  */
7364 Roo.namespace('Roo.bootstrap.breadcrumb');
7365
7366
7367 /**
7368  * @class Roo.bootstrap.breadcrumb.Nav
7369  * @extends Roo.bootstrap.Component
7370  * Bootstrap Breadcrumb Nav Class
7371  *  
7372  * @children Roo.bootstrap.breadcrumb.Item
7373  * 
7374  * @constructor
7375  * Create a new breadcrumb.Nav
7376  * @param {Object} config The config object
7377  */
7378
7379
7380 Roo.bootstrap.breadcrumb.Nav = function(config){
7381     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7382     
7383     
7384 };
7385
7386 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7387     
7388     getAutoCreate : function()
7389     {
7390
7391         var cfg = {
7392             tag: 'nav',
7393             cn : [
7394                 {
7395                     tag : 'ol',
7396                     cls : 'breadcrumb'
7397                 }
7398             ]
7399             
7400         };
7401           
7402         return cfg;
7403     },
7404     
7405     initEvents: function()
7406     {
7407         this.olEl = this.el.select('ol',true).first();    
7408     },
7409     getChildContainer : function()
7410     {
7411         return this.olEl;  
7412     }
7413     
7414 });
7415
7416  /*
7417  * - LGPL
7418  *
7419  *  Breadcrumb Item
7420  * 
7421  */
7422
7423
7424 /**
7425  * @class Roo.bootstrap.breadcrumb.Nav
7426  * @extends Roo.bootstrap.Component
7427  * @children Roo.bootstrap.Component
7428  * @parent Roo.bootstrap.breadcrumb.Nav
7429  * Bootstrap Breadcrumb Nav Class
7430  *  
7431  * 
7432  * @cfg {String} html the content of the link.
7433  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7434  * @cfg {Boolean} active is it active
7435
7436  * 
7437  * @constructor
7438  * Create a new breadcrumb.Nav
7439  * @param {Object} config The config object
7440  */
7441
7442 Roo.bootstrap.breadcrumb.Item = function(config){
7443     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7444     this.addEvents({
7445         // img events
7446         /**
7447          * @event click
7448          * The img click event for the img.
7449          * @param {Roo.EventObject} e
7450          */
7451         "click" : true
7452     });
7453     
7454 };
7455
7456 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7457     
7458     href: false,
7459     html : '',
7460     
7461     getAutoCreate : function()
7462     {
7463
7464         var cfg = {
7465             tag: 'li',
7466             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7467         };
7468         if (this.href !== false) {
7469             cfg.cn = [{
7470                 tag : 'a',
7471                 href : this.href,
7472                 html : this.html
7473             }];
7474         } else {
7475             cfg.html = this.html;
7476         }
7477         
7478         return cfg;
7479     },
7480     
7481     initEvents: function()
7482     {
7483         if (this.href) {
7484             this.el.select('a', true).first().on('click',this.onClick, this)
7485         }
7486         
7487     },
7488     onClick : function(e)
7489     {
7490         e.preventDefault();
7491         this.fireEvent('click',this,  e);
7492     }
7493     
7494 });
7495
7496  /*
7497  * - LGPL
7498  *
7499  * row
7500  * 
7501  */
7502
7503 /**
7504  * @class Roo.bootstrap.Row
7505  * @extends Roo.bootstrap.Component
7506  * @children Roo.bootstrap.Component
7507  * Bootstrap Row class (contains columns...)
7508  * 
7509  * @constructor
7510  * Create a new Row
7511  * @param {Object} config The config object
7512  */
7513
7514 Roo.bootstrap.Row = function(config){
7515     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7516 };
7517
7518 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7519     
7520     getAutoCreate : function(){
7521        return {
7522             cls: 'row clearfix'
7523        };
7524     }
7525     
7526     
7527 });
7528
7529  
7530
7531  /*
7532  * - LGPL
7533  *
7534  * pagination
7535  * 
7536  */
7537
7538 /**
7539  * @class Roo.bootstrap.Pagination
7540  * @extends Roo.bootstrap.Component
7541  * @children Roo.bootstrap.Pagination
7542  * Bootstrap Pagination class
7543  * 
7544  * @cfg {String} size (xs|sm|md|lg|xl)
7545  * @cfg {Boolean} inverse 
7546  * 
7547  * @constructor
7548  * Create a new Pagination
7549  * @param {Object} config The config object
7550  */
7551
7552 Roo.bootstrap.Pagination = function(config){
7553     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7554 };
7555
7556 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7557     
7558     cls: false,
7559     size: false,
7560     inverse: false,
7561     
7562     getAutoCreate : function(){
7563         var cfg = {
7564             tag: 'ul',
7565                 cls: 'pagination'
7566         };
7567         if (this.inverse) {
7568             cfg.cls += ' inverse';
7569         }
7570         if (this.html) {
7571             cfg.html=this.html;
7572         }
7573         if (this.cls) {
7574             cfg.cls += " " + this.cls;
7575         }
7576         return cfg;
7577     }
7578    
7579 });
7580
7581  
7582
7583  /*
7584  * - LGPL
7585  *
7586  * Pagination item
7587  * 
7588  */
7589
7590
7591 /**
7592  * @class Roo.bootstrap.PaginationItem
7593  * @extends Roo.bootstrap.Component
7594  * Bootstrap PaginationItem class
7595  * @cfg {String} html text
7596  * @cfg {String} href the link
7597  * @cfg {Boolean} preventDefault (true | false) default true
7598  * @cfg {Boolean} active (true | false) default false
7599  * @cfg {Boolean} disabled default false
7600  * 
7601  * 
7602  * @constructor
7603  * Create a new PaginationItem
7604  * @param {Object} config The config object
7605  */
7606
7607
7608 Roo.bootstrap.PaginationItem = function(config){
7609     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7610     this.addEvents({
7611         // raw events
7612         /**
7613          * @event click
7614          * The raw click event for the entire grid.
7615          * @param {Roo.EventObject} e
7616          */
7617         "click" : true
7618     });
7619 };
7620
7621 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7622     
7623     href : false,
7624     html : false,
7625     preventDefault: true,
7626     active : false,
7627     cls : false,
7628     disabled: false,
7629     
7630     getAutoCreate : function(){
7631         var cfg= {
7632             tag: 'li',
7633             cn: [
7634                 {
7635                     tag : 'a',
7636                     href : this.href ? this.href : '#',
7637                     html : this.html ? this.html : ''
7638                 }
7639             ]
7640         };
7641         
7642         if(this.cls){
7643             cfg.cls = this.cls;
7644         }
7645         
7646         if(this.disabled){
7647             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7648         }
7649         
7650         if(this.active){
7651             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7652         }
7653         
7654         return cfg;
7655     },
7656     
7657     initEvents: function() {
7658         
7659         this.el.on('click', this.onClick, this);
7660         
7661     },
7662     onClick : function(e)
7663     {
7664         Roo.log('PaginationItem on click ');
7665         if(this.preventDefault){
7666             e.preventDefault();
7667         }
7668         
7669         if(this.disabled){
7670             return;
7671         }
7672         
7673         this.fireEvent('click', this, e);
7674     }
7675    
7676 });
7677
7678  
7679
7680  /*
7681  * - LGPL
7682  *
7683  * slider
7684  * 
7685  */
7686
7687
7688 /**
7689  * @class Roo.bootstrap.Slider
7690  * @extends Roo.bootstrap.Component
7691  * Bootstrap Slider class
7692  *    
7693  * @constructor
7694  * Create a new Slider
7695  * @param {Object} config The config object
7696  */
7697
7698 Roo.bootstrap.Slider = function(config){
7699     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7700 };
7701
7702 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7703     
7704     getAutoCreate : function(){
7705         
7706         var cfg = {
7707             tag: 'div',
7708             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7709             cn: [
7710                 {
7711                     tag: 'a',
7712                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7713                 }
7714             ]
7715         };
7716         
7717         return cfg;
7718     }
7719    
7720 });
7721
7722  /*
7723  * Based on:
7724  * Ext JS Library 1.1.1
7725  * Copyright(c) 2006-2007, Ext JS, LLC.
7726  *
7727  * Originally Released Under LGPL - original licence link has changed is not relivant.
7728  *
7729  * Fork - LGPL
7730  * <script type="text/javascript">
7731  */
7732  /**
7733  * @extends Roo.dd.DDProxy
7734  * @class Roo.grid.SplitDragZone
7735  * Support for Column Header resizing
7736  * @constructor
7737  * @param {Object} config
7738  */
7739 // private
7740 // This is a support class used internally by the Grid components
7741 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7742     this.grid = grid;
7743     this.view = grid.getView();
7744     this.proxy = this.view.resizeProxy;
7745     Roo.grid.SplitDragZone.superclass.constructor.call(
7746         this,
7747         hd, // ID
7748         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7749         {  // CONFIG
7750             dragElId : Roo.id(this.proxy.dom),
7751             resizeFrame:false
7752         }
7753     );
7754     
7755     this.setHandleElId(Roo.id(hd));
7756     if (hd2 !== false) {
7757         this.setOuterHandleElId(Roo.id(hd2));
7758     }
7759     
7760     this.scroll = false;
7761 };
7762 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7763     fly: Roo.Element.fly,
7764
7765     b4StartDrag : function(x, y){
7766         this.view.headersDisabled = true;
7767         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7768                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7769         );
7770         this.proxy.setHeight(h);
7771         
7772         // for old system colWidth really stored the actual width?
7773         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7774         // which in reality did not work.. - it worked only for fixed sizes
7775         // for resizable we need to use actual sizes.
7776         var w = this.cm.getColumnWidth(this.cellIndex);
7777         if (!this.view.mainWrap) {
7778             // bootstrap.
7779             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7780         }
7781         
7782         
7783         
7784         // this was w-this.grid.minColumnWidth;
7785         // doesnt really make sense? - w = thie curren width or the rendered one?
7786         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7787         this.resetConstraints();
7788         this.setXConstraint(minw, 1000);
7789         this.setYConstraint(0, 0);
7790         this.minX = x - minw;
7791         this.maxX = x + 1000;
7792         this.startPos = x;
7793         if (!this.view.mainWrap) { // this is Bootstrap code..
7794             this.getDragEl().style.display='block';
7795         }
7796         
7797         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7798     },
7799
7800
7801     handleMouseDown : function(e){
7802         ev = Roo.EventObject.setEvent(e);
7803         var t = this.fly(ev.getTarget());
7804         if(t.hasClass("x-grid-split")){
7805             this.cellIndex = this.view.getCellIndex(t.dom);
7806             this.split = t.dom;
7807             this.cm = this.grid.colModel;
7808             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7809                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7810             }
7811         }
7812     },
7813
7814     endDrag : function(e){
7815         this.view.headersDisabled = false;
7816         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7817         var diff = endX - this.startPos;
7818         // 
7819         var w = this.cm.getColumnWidth(this.cellIndex);
7820         if (!this.view.mainWrap) {
7821             w = 0;
7822         }
7823         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7824     },
7825
7826     autoOffset : function(){
7827         this.setDelta(0,0);
7828     }
7829 });/*
7830  * Based on:
7831  * Ext JS Library 1.1.1
7832  * Copyright(c) 2006-2007, Ext JS, LLC.
7833  *
7834  * Originally Released Under LGPL - original licence link has changed is not relivant.
7835  *
7836  * Fork - LGPL
7837  * <script type="text/javascript">
7838  */
7839
7840 /**
7841  * @class Roo.grid.AbstractSelectionModel
7842  * @extends Roo.util.Observable
7843  * @abstract
7844  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7845  * implemented by descendant classes.  This class should not be directly instantiated.
7846  * @constructor
7847  */
7848 Roo.grid.AbstractSelectionModel = function(){
7849     this.locked = false;
7850     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7851 };
7852
7853 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7854     /** @ignore Called by the grid automatically. Do not call directly. */
7855     init : function(grid){
7856         this.grid = grid;
7857         this.initEvents();
7858     },
7859
7860     /**
7861      * Locks the selections.
7862      */
7863     lock : function(){
7864         this.locked = true;
7865     },
7866
7867     /**
7868      * Unlocks the selections.
7869      */
7870     unlock : function(){
7871         this.locked = false;
7872     },
7873
7874     /**
7875      * Returns true if the selections are locked.
7876      * @return {Boolean}
7877      */
7878     isLocked : function(){
7879         return this.locked;
7880     }
7881 });/*
7882  * Based on:
7883  * Ext JS Library 1.1.1
7884  * Copyright(c) 2006-2007, Ext JS, LLC.
7885  *
7886  * Originally Released Under LGPL - original licence link has changed is not relivant.
7887  *
7888  * Fork - LGPL
7889  * <script type="text/javascript">
7890  */
7891 /**
7892  * @extends Roo.grid.AbstractSelectionModel
7893  * @class Roo.grid.RowSelectionModel
7894  * The default SelectionModel used by {@link Roo.grid.Grid}.
7895  * It supports multiple selections and keyboard selection/navigation. 
7896  * @constructor
7897  * @param {Object} config
7898  */
7899 Roo.grid.RowSelectionModel = function(config){
7900     Roo.apply(this, config);
7901     this.selections = new Roo.util.MixedCollection(false, function(o){
7902         return o.id;
7903     });
7904
7905     this.last = false;
7906     this.lastActive = false;
7907
7908     this.addEvents({
7909         /**
7910         * @event selectionchange
7911         * Fires when the selection changes
7912         * @param {SelectionModel} this
7913         */
7914        "selectionchange" : true,
7915        /**
7916         * @event afterselectionchange
7917         * Fires after the selection changes (eg. by key press or clicking)
7918         * @param {SelectionModel} this
7919         */
7920        "afterselectionchange" : true,
7921        /**
7922         * @event beforerowselect
7923         * Fires when a row is selected being selected, return false to cancel.
7924         * @param {SelectionModel} this
7925         * @param {Number} rowIndex The selected index
7926         * @param {Boolean} keepExisting False if other selections will be cleared
7927         */
7928        "beforerowselect" : true,
7929        /**
7930         * @event rowselect
7931         * Fires when a row is selected.
7932         * @param {SelectionModel} this
7933         * @param {Number} rowIndex The selected index
7934         * @param {Roo.data.Record} r The record
7935         */
7936        "rowselect" : true,
7937        /**
7938         * @event rowdeselect
7939         * Fires when a row is deselected.
7940         * @param {SelectionModel} this
7941         * @param {Number} rowIndex The selected index
7942         */
7943         "rowdeselect" : true
7944     });
7945     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7946     this.locked = false;
7947 };
7948
7949 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7950     /**
7951      * @cfg {Boolean} singleSelect
7952      * True to allow selection of only one row at a time (defaults to false)
7953      */
7954     singleSelect : false,
7955
7956     // private
7957     initEvents : function(){
7958
7959         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7960             this.grid.on("mousedown", this.handleMouseDown, this);
7961         }else{ // allow click to work like normal
7962             this.grid.on("rowclick", this.handleDragableRowClick, this);
7963         }
7964         // bootstrap does not have a view..
7965         var view = this.grid.view ? this.grid.view : this.grid;
7966         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7967             "up" : function(e){
7968                 if(!e.shiftKey){
7969                     this.selectPrevious(e.shiftKey);
7970                 }else if(this.last !== false && this.lastActive !== false){
7971                     var last = this.last;
7972                     this.selectRange(this.last,  this.lastActive-1);
7973                     view.focusRow(this.lastActive);
7974                     if(last !== false){
7975                         this.last = last;
7976                     }
7977                 }else{
7978                     this.selectFirstRow();
7979                 }
7980                 this.fireEvent("afterselectionchange", this);
7981             },
7982             "down" : function(e){
7983                 if(!e.shiftKey){
7984                     this.selectNext(e.shiftKey);
7985                 }else if(this.last !== false && this.lastActive !== false){
7986                     var last = this.last;
7987                     this.selectRange(this.last,  this.lastActive+1);
7988                     view.focusRow(this.lastActive);
7989                     if(last !== false){
7990                         this.last = last;
7991                     }
7992                 }else{
7993                     this.selectFirstRow();
7994                 }
7995                 this.fireEvent("afterselectionchange", this);
7996             },
7997             scope: this
7998         });
7999
8000          
8001         view.on("refresh", this.onRefresh, this);
8002         view.on("rowupdated", this.onRowUpdated, this);
8003         view.on("rowremoved", this.onRemove, this);
8004     },
8005
8006     // private
8007     onRefresh : function(){
8008         var ds = this.grid.ds, i, v = this.grid.view;
8009         var s = this.selections;
8010         s.each(function(r){
8011             if((i = ds.indexOfId(r.id)) != -1){
8012                 v.onRowSelect(i);
8013                 s.add(ds.getAt(i)); // updating the selection relate data
8014             }else{
8015                 s.remove(r);
8016             }
8017         });
8018     },
8019
8020     // private
8021     onRemove : function(v, index, r){
8022         this.selections.remove(r);
8023     },
8024
8025     // private
8026     onRowUpdated : function(v, index, r){
8027         if(this.isSelected(r)){
8028             v.onRowSelect(index);
8029         }
8030     },
8031
8032     /**
8033      * Select records.
8034      * @param {Array} records The records to select
8035      * @param {Boolean} keepExisting (optional) True to keep existing selections
8036      */
8037     selectRecords : function(records, keepExisting){
8038         if(!keepExisting){
8039             this.clearSelections();
8040         }
8041         var ds = this.grid.ds;
8042         for(var i = 0, len = records.length; i < len; i++){
8043             this.selectRow(ds.indexOf(records[i]), true);
8044         }
8045     },
8046
8047     /**
8048      * Gets the number of selected rows.
8049      * @return {Number}
8050      */
8051     getCount : function(){
8052         return this.selections.length;
8053     },
8054
8055     /**
8056      * Selects the first row in the grid.
8057      */
8058     selectFirstRow : function(){
8059         this.selectRow(0);
8060     },
8061
8062     /**
8063      * Select the last row.
8064      * @param {Boolean} keepExisting (optional) True to keep existing selections
8065      */
8066     selectLastRow : function(keepExisting){
8067         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8068     },
8069
8070     /**
8071      * Selects the row immediately following the last selected row.
8072      * @param {Boolean} keepExisting (optional) True to keep existing selections
8073      */
8074     selectNext : function(keepExisting){
8075         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8076             this.selectRow(this.last+1, keepExisting);
8077             var view = this.grid.view ? this.grid.view : this.grid;
8078             view.focusRow(this.last);
8079         }
8080     },
8081
8082     /**
8083      * Selects the row that precedes the last selected row.
8084      * @param {Boolean} keepExisting (optional) True to keep existing selections
8085      */
8086     selectPrevious : function(keepExisting){
8087         if(this.last){
8088             this.selectRow(this.last-1, keepExisting);
8089             var view = this.grid.view ? this.grid.view : this.grid;
8090             view.focusRow(this.last);
8091         }
8092     },
8093
8094     /**
8095      * Returns the selected records
8096      * @return {Array} Array of selected records
8097      */
8098     getSelections : function(){
8099         return [].concat(this.selections.items);
8100     },
8101
8102     /**
8103      * Returns the first selected record.
8104      * @return {Record}
8105      */
8106     getSelected : function(){
8107         return this.selections.itemAt(0);
8108     },
8109
8110
8111     /**
8112      * Clears all selections.
8113      */
8114     clearSelections : function(fast){
8115         if(this.locked) {
8116             return;
8117         }
8118         if(fast !== true){
8119             var ds = this.grid.ds;
8120             var s = this.selections;
8121             s.each(function(r){
8122                 this.deselectRow(ds.indexOfId(r.id));
8123             }, this);
8124             s.clear();
8125         }else{
8126             this.selections.clear();
8127         }
8128         this.last = false;
8129     },
8130
8131
8132     /**
8133      * Selects all rows.
8134      */
8135     selectAll : function(){
8136         if(this.locked) {
8137             return;
8138         }
8139         this.selections.clear();
8140         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8141             this.selectRow(i, true);
8142         }
8143     },
8144
8145     /**
8146      * Returns True if there is a selection.
8147      * @return {Boolean}
8148      */
8149     hasSelection : function(){
8150         return this.selections.length > 0;
8151     },
8152
8153     /**
8154      * Returns True if the specified row is selected.
8155      * @param {Number/Record} record The record or index of the record to check
8156      * @return {Boolean}
8157      */
8158     isSelected : function(index){
8159         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8160         return (r && this.selections.key(r.id) ? true : false);
8161     },
8162
8163     /**
8164      * Returns True if the specified record id is selected.
8165      * @param {String} id The id of record to check
8166      * @return {Boolean}
8167      */
8168     isIdSelected : function(id){
8169         return (this.selections.key(id) ? true : false);
8170     },
8171
8172     // private
8173     handleMouseDown : function(e, t)
8174     {
8175         var view = this.grid.view ? this.grid.view : this.grid;
8176         var rowIndex;
8177         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8178             return;
8179         };
8180         if(e.shiftKey && this.last !== false){
8181             var last = this.last;
8182             this.selectRange(last, rowIndex, e.ctrlKey);
8183             this.last = last; // reset the last
8184             view.focusRow(rowIndex);
8185         }else{
8186             var isSelected = this.isSelected(rowIndex);
8187             if(e.button !== 0 && isSelected){
8188                 view.focusRow(rowIndex);
8189             }else if(e.ctrlKey && isSelected){
8190                 this.deselectRow(rowIndex);
8191             }else if(!isSelected){
8192                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8193                 view.focusRow(rowIndex);
8194             }
8195         }
8196         this.fireEvent("afterselectionchange", this);
8197     },
8198     // private
8199     handleDragableRowClick :  function(grid, rowIndex, e) 
8200     {
8201         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8202             this.selectRow(rowIndex, false);
8203             var view = this.grid.view ? this.grid.view : this.grid;
8204             view.focusRow(rowIndex);
8205              this.fireEvent("afterselectionchange", this);
8206         }
8207     },
8208     
8209     /**
8210      * Selects multiple rows.
8211      * @param {Array} rows Array of the indexes of the row to select
8212      * @param {Boolean} keepExisting (optional) True to keep existing selections
8213      */
8214     selectRows : function(rows, keepExisting){
8215         if(!keepExisting){
8216             this.clearSelections();
8217         }
8218         for(var i = 0, len = rows.length; i < len; i++){
8219             this.selectRow(rows[i], true);
8220         }
8221     },
8222
8223     /**
8224      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8225      * @param {Number} startRow The index of the first row in the range
8226      * @param {Number} endRow The index of the last row in the range
8227      * @param {Boolean} keepExisting (optional) True to retain existing selections
8228      */
8229     selectRange : function(startRow, endRow, keepExisting){
8230         if(this.locked) {
8231             return;
8232         }
8233         if(!keepExisting){
8234             this.clearSelections();
8235         }
8236         if(startRow <= endRow){
8237             for(var i = startRow; i <= endRow; i++){
8238                 this.selectRow(i, true);
8239             }
8240         }else{
8241             for(var i = startRow; i >= endRow; i--){
8242                 this.selectRow(i, true);
8243             }
8244         }
8245     },
8246
8247     /**
8248      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8249      * @param {Number} startRow The index of the first row in the range
8250      * @param {Number} endRow The index of the last row in the range
8251      */
8252     deselectRange : function(startRow, endRow, preventViewNotify){
8253         if(this.locked) {
8254             return;
8255         }
8256         for(var i = startRow; i <= endRow; i++){
8257             this.deselectRow(i, preventViewNotify);
8258         }
8259     },
8260
8261     /**
8262      * Selects a row.
8263      * @param {Number} row The index of the row to select
8264      * @param {Boolean} keepExisting (optional) True to keep existing selections
8265      */
8266     selectRow : function(index, keepExisting, preventViewNotify){
8267         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8268             return;
8269         }
8270         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8271             if(!keepExisting || this.singleSelect){
8272                 this.clearSelections();
8273             }
8274             var r = this.grid.ds.getAt(index);
8275             this.selections.add(r);
8276             this.last = this.lastActive = index;
8277             if(!preventViewNotify){
8278                 var view = this.grid.view ? this.grid.view : this.grid;
8279                 view.onRowSelect(index);
8280             }
8281             this.fireEvent("rowselect", this, index, r);
8282             this.fireEvent("selectionchange", this);
8283         }
8284     },
8285
8286     /**
8287      * Deselects a row.
8288      * @param {Number} row The index of the row to deselect
8289      */
8290     deselectRow : function(index, preventViewNotify){
8291         if(this.locked) {
8292             return;
8293         }
8294         if(this.last == index){
8295             this.last = false;
8296         }
8297         if(this.lastActive == index){
8298             this.lastActive = false;
8299         }
8300         var r = this.grid.ds.getAt(index);
8301         this.selections.remove(r);
8302         if(!preventViewNotify){
8303             var view = this.grid.view ? this.grid.view : this.grid;
8304             view.onRowDeselect(index);
8305         }
8306         this.fireEvent("rowdeselect", this, index);
8307         this.fireEvent("selectionchange", this);
8308     },
8309
8310     // private
8311     restoreLast : function(){
8312         if(this._last){
8313             this.last = this._last;
8314         }
8315     },
8316
8317     // private
8318     acceptsNav : function(row, col, cm){
8319         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8320     },
8321
8322     // private
8323     onEditorKey : function(field, e){
8324         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8325         if(k == e.TAB){
8326             e.stopEvent();
8327             ed.completeEdit();
8328             if(e.shiftKey){
8329                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8330             }else{
8331                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8332             }
8333         }else if(k == e.ENTER && !e.ctrlKey){
8334             e.stopEvent();
8335             ed.completeEdit();
8336             if(e.shiftKey){
8337                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8338             }else{
8339                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8340             }
8341         }else if(k == e.ESC){
8342             ed.cancelEdit();
8343         }
8344         if(newCell){
8345             g.startEditing(newCell[0], newCell[1]);
8346         }
8347     }
8348 });/*
8349  * Based on:
8350  * Ext JS Library 1.1.1
8351  * Copyright(c) 2006-2007, Ext JS, LLC.
8352  *
8353  * Originally Released Under LGPL - original licence link has changed is not relivant.
8354  *
8355  * Fork - LGPL
8356  * <script type="text/javascript">
8357  */
8358  
8359
8360 /**
8361  * @class Roo.grid.ColumnModel
8362  * @extends Roo.util.Observable
8363  * This is the default implementation of a ColumnModel used by the Grid. It defines
8364  * the columns in the grid.
8365  * <br>Usage:<br>
8366  <pre><code>
8367  var colModel = new Roo.grid.ColumnModel([
8368         {header: "Ticker", width: 60, sortable: true, locked: true},
8369         {header: "Company Name", width: 150, sortable: true},
8370         {header: "Market Cap.", width: 100, sortable: true},
8371         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8372         {header: "Employees", width: 100, sortable: true, resizable: false}
8373  ]);
8374  </code></pre>
8375  * <p>
8376  
8377  * The config options listed for this class are options which may appear in each
8378  * individual column definition.
8379  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8380  * @constructor
8381  * @param {Object} config An Array of column config objects. See this class's
8382  * config objects for details.
8383 */
8384 Roo.grid.ColumnModel = function(config){
8385         /**
8386      * The config passed into the constructor
8387      */
8388     this.config = []; //config;
8389     this.lookup = {};
8390
8391     // if no id, create one
8392     // if the column does not have a dataIndex mapping,
8393     // map it to the order it is in the config
8394     for(var i = 0, len = config.length; i < len; i++){
8395         this.addColumn(config[i]);
8396         
8397     }
8398
8399     /**
8400      * The width of columns which have no width specified (defaults to 100)
8401      * @type Number
8402      */
8403     this.defaultWidth = 100;
8404
8405     /**
8406      * Default sortable of columns which have no sortable specified (defaults to false)
8407      * @type Boolean
8408      */
8409     this.defaultSortable = false;
8410
8411     this.addEvents({
8412         /**
8413              * @event widthchange
8414              * Fires when the width of a column changes.
8415              * @param {ColumnModel} this
8416              * @param {Number} columnIndex The column index
8417              * @param {Number} newWidth The new width
8418              */
8419             "widthchange": true,
8420         /**
8421              * @event headerchange
8422              * Fires when the text of a header changes.
8423              * @param {ColumnModel} this
8424              * @param {Number} columnIndex The column index
8425              * @param {Number} newText The new header text
8426              */
8427             "headerchange": true,
8428         /**
8429              * @event hiddenchange
8430              * Fires when a column is hidden or "unhidden".
8431              * @param {ColumnModel} this
8432              * @param {Number} columnIndex The column index
8433              * @param {Boolean} hidden true if hidden, false otherwise
8434              */
8435             "hiddenchange": true,
8436             /**
8437          * @event columnmoved
8438          * Fires when a column is moved.
8439          * @param {ColumnModel} this
8440          * @param {Number} oldIndex
8441          * @param {Number} newIndex
8442          */
8443         "columnmoved" : true,
8444         /**
8445          * @event columlockchange
8446          * Fires when a column's locked state is changed
8447          * @param {ColumnModel} this
8448          * @param {Number} colIndex
8449          * @param {Boolean} locked true if locked
8450          */
8451         "columnlockchange" : true
8452     });
8453     Roo.grid.ColumnModel.superclass.constructor.call(this);
8454 };
8455 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8456     /**
8457      * @cfg {String} header The header text to display in the Grid view.
8458      */
8459         /**
8460      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8461      */
8462         /**
8463      * @cfg {String} smHeader Header at Bootsrap Small width
8464      */
8465         /**
8466      * @cfg {String} mdHeader Header at Bootsrap Medium width
8467      */
8468         /**
8469      * @cfg {String} lgHeader Header at Bootsrap Large width
8470      */
8471         /**
8472      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8473      */
8474     /**
8475      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8476      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8477      * specified, the column's index is used as an index into the Record's data Array.
8478      */
8479     /**
8480      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8481      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8482      */
8483     /**
8484      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8485      * Defaults to the value of the {@link #defaultSortable} property.
8486      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8487      */
8488     /**
8489      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8490      */
8491     /**
8492      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8493      */
8494     /**
8495      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8496      */
8497     /**
8498      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8499      */
8500     /**
8501      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8502      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8503      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8504      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8505      */
8506        /**
8507      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8508      */
8509     /**
8510      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8511      */
8512     /**
8513      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8514      */
8515     /**
8516      * @cfg {String} cursor (Optional)
8517      */
8518     /**
8519      * @cfg {String} tooltip (Optional)
8520      */
8521     /**
8522      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8523      */
8524     /**
8525      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8526      */
8527     /**
8528      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8529      */
8530     /**
8531      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8532      */
8533         /**
8534      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8535      */
8536     /**
8537      * Returns the id of the column at the specified index.
8538      * @param {Number} index The column index
8539      * @return {String} the id
8540      */
8541     getColumnId : function(index){
8542         return this.config[index].id;
8543     },
8544
8545     /**
8546      * Returns the column for a specified id.
8547      * @param {String} id The column id
8548      * @return {Object} the column
8549      */
8550     getColumnById : function(id){
8551         return this.lookup[id];
8552     },
8553
8554     
8555     /**
8556      * Returns the column Object for a specified dataIndex.
8557      * @param {String} dataIndex The column dataIndex
8558      * @return {Object|Boolean} the column or false if not found
8559      */
8560     getColumnByDataIndex: function(dataIndex){
8561         var index = this.findColumnIndex(dataIndex);
8562         return index > -1 ? this.config[index] : false;
8563     },
8564     
8565     /**
8566      * Returns the index for a specified column id.
8567      * @param {String} id The column id
8568      * @return {Number} the index, or -1 if not found
8569      */
8570     getIndexById : function(id){
8571         for(var i = 0, len = this.config.length; i < len; i++){
8572             if(this.config[i].id == id){
8573                 return i;
8574             }
8575         }
8576         return -1;
8577     },
8578     
8579     /**
8580      * Returns the index for a specified column dataIndex.
8581      * @param {String} dataIndex The column dataIndex
8582      * @return {Number} the index, or -1 if not found
8583      */
8584     
8585     findColumnIndex : function(dataIndex){
8586         for(var i = 0, len = this.config.length; i < len; i++){
8587             if(this.config[i].dataIndex == dataIndex){
8588                 return i;
8589             }
8590         }
8591         return -1;
8592     },
8593     
8594     
8595     moveColumn : function(oldIndex, newIndex){
8596         var c = this.config[oldIndex];
8597         this.config.splice(oldIndex, 1);
8598         this.config.splice(newIndex, 0, c);
8599         this.dataMap = null;
8600         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8601     },
8602
8603     isLocked : function(colIndex){
8604         return this.config[colIndex].locked === true;
8605     },
8606
8607     setLocked : function(colIndex, value, suppressEvent){
8608         if(this.isLocked(colIndex) == value){
8609             return;
8610         }
8611         this.config[colIndex].locked = value;
8612         if(!suppressEvent){
8613             this.fireEvent("columnlockchange", this, colIndex, value);
8614         }
8615     },
8616
8617     getTotalLockedWidth : function(){
8618         var totalWidth = 0;
8619         for(var i = 0; i < this.config.length; i++){
8620             if(this.isLocked(i) && !this.isHidden(i)){
8621                 this.totalWidth += this.getColumnWidth(i);
8622             }
8623         }
8624         return totalWidth;
8625     },
8626
8627     getLockedCount : function(){
8628         for(var i = 0, len = this.config.length; i < len; i++){
8629             if(!this.isLocked(i)){
8630                 return i;
8631             }
8632         }
8633         
8634         return this.config.length;
8635     },
8636
8637     /**
8638      * Returns the number of columns.
8639      * @return {Number}
8640      */
8641     getColumnCount : function(visibleOnly){
8642         if(visibleOnly === true){
8643             var c = 0;
8644             for(var i = 0, len = this.config.length; i < len; i++){
8645                 if(!this.isHidden(i)){
8646                     c++;
8647                 }
8648             }
8649             return c;
8650         }
8651         return this.config.length;
8652     },
8653
8654     /**
8655      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8656      * @param {Function} fn
8657      * @param {Object} scope (optional)
8658      * @return {Array} result
8659      */
8660     getColumnsBy : function(fn, scope){
8661         var r = [];
8662         for(var i = 0, len = this.config.length; i < len; i++){
8663             var c = this.config[i];
8664             if(fn.call(scope||this, c, i) === true){
8665                 r[r.length] = c;
8666             }
8667         }
8668         return r;
8669     },
8670
8671     /**
8672      * Returns true if the specified column is sortable.
8673      * @param {Number} col The column index
8674      * @return {Boolean}
8675      */
8676     isSortable : function(col){
8677         if(typeof this.config[col].sortable == "undefined"){
8678             return this.defaultSortable;
8679         }
8680         return this.config[col].sortable;
8681     },
8682
8683     /**
8684      * Returns the rendering (formatting) function defined for the column.
8685      * @param {Number} col The column index.
8686      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8687      */
8688     getRenderer : function(col){
8689         if(!this.config[col].renderer){
8690             return Roo.grid.ColumnModel.defaultRenderer;
8691         }
8692         return this.config[col].renderer;
8693     },
8694
8695     /**
8696      * Sets the rendering (formatting) function for a column.
8697      * @param {Number} col The column index
8698      * @param {Function} fn The function to use to process the cell's raw data
8699      * to return HTML markup for the grid view. The render function is called with
8700      * the following parameters:<ul>
8701      * <li>Data value.</li>
8702      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8703      * <li>css A CSS style string to apply to the table cell.</li>
8704      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8705      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8706      * <li>Row index</li>
8707      * <li>Column index</li>
8708      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8709      */
8710     setRenderer : function(col, fn){
8711         this.config[col].renderer = fn;
8712     },
8713
8714     /**
8715      * Returns the width for the specified column.
8716      * @param {Number} col The column index
8717      * @param (optional) {String} gridSize bootstrap width size.
8718      * @return {Number}
8719      */
8720     getColumnWidth : function(col, gridSize)
8721         {
8722                 var cfg = this.config[col];
8723                 
8724                 if (typeof(gridSize) == 'undefined') {
8725                         return cfg.width * 1 || this.defaultWidth;
8726                 }
8727                 if (gridSize === false) { // if we set it..
8728                         return cfg.width || false;
8729                 }
8730                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8731                 
8732                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8733                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8734                                 continue;
8735                         }
8736                         return cfg[ sizes[i] ];
8737                 }
8738                 return 1;
8739                 
8740     },
8741
8742     /**
8743      * Sets the width for a column.
8744      * @param {Number} col The column index
8745      * @param {Number} width The new width
8746      */
8747     setColumnWidth : function(col, width, suppressEvent){
8748         this.config[col].width = width;
8749         this.totalWidth = null;
8750         if(!suppressEvent){
8751              this.fireEvent("widthchange", this, col, width);
8752         }
8753     },
8754
8755     /**
8756      * Returns the total width of all columns.
8757      * @param {Boolean} includeHidden True to include hidden column widths
8758      * @return {Number}
8759      */
8760     getTotalWidth : function(includeHidden){
8761         if(!this.totalWidth){
8762             this.totalWidth = 0;
8763             for(var i = 0, len = this.config.length; i < len; i++){
8764                 if(includeHidden || !this.isHidden(i)){
8765                     this.totalWidth += this.getColumnWidth(i);
8766                 }
8767             }
8768         }
8769         return this.totalWidth;
8770     },
8771
8772     /**
8773      * Returns the header for the specified column.
8774      * @param {Number} col The column index
8775      * @return {String}
8776      */
8777     getColumnHeader : function(col){
8778         return this.config[col].header;
8779     },
8780
8781     /**
8782      * Sets the header for a column.
8783      * @param {Number} col The column index
8784      * @param {String} header The new header
8785      */
8786     setColumnHeader : function(col, header){
8787         this.config[col].header = header;
8788         this.fireEvent("headerchange", this, col, header);
8789     },
8790
8791     /**
8792      * Returns the tooltip for the specified column.
8793      * @param {Number} col The column index
8794      * @return {String}
8795      */
8796     getColumnTooltip : function(col){
8797             return this.config[col].tooltip;
8798     },
8799     /**
8800      * Sets the tooltip for a column.
8801      * @param {Number} col The column index
8802      * @param {String} tooltip The new tooltip
8803      */
8804     setColumnTooltip : function(col, tooltip){
8805             this.config[col].tooltip = tooltip;
8806     },
8807
8808     /**
8809      * Returns the dataIndex for the specified column.
8810      * @param {Number} col The column index
8811      * @return {Number}
8812      */
8813     getDataIndex : function(col){
8814         return this.config[col].dataIndex;
8815     },
8816
8817     /**
8818      * Sets the dataIndex for a column.
8819      * @param {Number} col The column index
8820      * @param {Number} dataIndex The new dataIndex
8821      */
8822     setDataIndex : function(col, dataIndex){
8823         this.config[col].dataIndex = dataIndex;
8824     },
8825
8826     
8827     
8828     /**
8829      * Returns true if the cell is editable.
8830      * @param {Number} colIndex The column index
8831      * @param {Number} rowIndex The row index - this is nto actually used..?
8832      * @return {Boolean}
8833      */
8834     isCellEditable : function(colIndex, rowIndex){
8835         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8836     },
8837
8838     /**
8839      * Returns the editor defined for the cell/column.
8840      * return false or null to disable editing.
8841      * @param {Number} colIndex The column index
8842      * @param {Number} rowIndex The row index
8843      * @return {Object}
8844      */
8845     getCellEditor : function(colIndex, rowIndex){
8846         return this.config[colIndex].editor;
8847     },
8848
8849     /**
8850      * Sets if a column is editable.
8851      * @param {Number} col The column index
8852      * @param {Boolean} editable True if the column is editable
8853      */
8854     setEditable : function(col, editable){
8855         this.config[col].editable = editable;
8856     },
8857
8858
8859     /**
8860      * Returns true if the column is hidden.
8861      * @param {Number} colIndex The column index
8862      * @return {Boolean}
8863      */
8864     isHidden : function(colIndex){
8865         return this.config[colIndex].hidden;
8866     },
8867
8868
8869     /**
8870      * Returns true if the column width cannot be changed
8871      */
8872     isFixed : function(colIndex){
8873         return this.config[colIndex].fixed;
8874     },
8875
8876     /**
8877      * Returns true if the column can be resized
8878      * @return {Boolean}
8879      */
8880     isResizable : function(colIndex){
8881         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8882     },
8883     /**
8884      * Sets if a column is hidden.
8885      * @param {Number} colIndex The column index
8886      * @param {Boolean} hidden True if the column is hidden
8887      */
8888     setHidden : function(colIndex, hidden){
8889         this.config[colIndex].hidden = hidden;
8890         this.totalWidth = null;
8891         this.fireEvent("hiddenchange", this, colIndex, hidden);
8892     },
8893
8894     /**
8895      * Sets the editor for a column.
8896      * @param {Number} col The column index
8897      * @param {Object} editor The editor object
8898      */
8899     setEditor : function(col, editor){
8900         this.config[col].editor = editor;
8901     },
8902     /**
8903      * Add a column (experimental...) - defaults to adding to the end..
8904      * @param {Object} config 
8905     */
8906     addColumn : function(c)
8907     {
8908     
8909         var i = this.config.length;
8910         this.config[i] = c;
8911         
8912         if(typeof c.dataIndex == "undefined"){
8913             c.dataIndex = i;
8914         }
8915         if(typeof c.renderer == "string"){
8916             c.renderer = Roo.util.Format[c.renderer];
8917         }
8918         if(typeof c.id == "undefined"){
8919             c.id = Roo.id();
8920         }
8921         if(c.editor && c.editor.xtype){
8922             c.editor  = Roo.factory(c.editor, Roo.grid);
8923         }
8924         if(c.editor && c.editor.isFormField){
8925             c.editor = new Roo.grid.GridEditor(c.editor);
8926         }
8927         this.lookup[c.id] = c;
8928     }
8929     
8930 });
8931
8932 Roo.grid.ColumnModel.defaultRenderer = function(value)
8933 {
8934     if(typeof value == "object") {
8935         return value;
8936     }
8937         if(typeof value == "string" && value.length < 1){
8938             return "&#160;";
8939         }
8940     
8941         return String.format("{0}", value);
8942 };
8943
8944 // Alias for backwards compatibility
8945 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8946 /*
8947  * Based on:
8948  * Ext JS Library 1.1.1
8949  * Copyright(c) 2006-2007, Ext JS, LLC.
8950  *
8951  * Originally Released Under LGPL - original licence link has changed is not relivant.
8952  *
8953  * Fork - LGPL
8954  * <script type="text/javascript">
8955  */
8956  
8957 /**
8958  * @class Roo.LoadMask
8959  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8960  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8961  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8962  * element's UpdateManager load indicator and will be destroyed after the initial load.
8963  * @constructor
8964  * Create a new LoadMask
8965  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8966  * @param {Object} config The config object
8967  */
8968 Roo.LoadMask = function(el, config){
8969     this.el = Roo.get(el);
8970     Roo.apply(this, config);
8971     if(this.store){
8972         this.store.on('beforeload', this.onBeforeLoad, this);
8973         this.store.on('load', this.onLoad, this);
8974         this.store.on('loadexception', this.onLoadException, this);
8975         this.removeMask = false;
8976     }else{
8977         var um = this.el.getUpdateManager();
8978         um.showLoadIndicator = false; // disable the default indicator
8979         um.on('beforeupdate', this.onBeforeLoad, this);
8980         um.on('update', this.onLoad, this);
8981         um.on('failure', this.onLoad, this);
8982         this.removeMask = true;
8983     }
8984 };
8985
8986 Roo.LoadMask.prototype = {
8987     /**
8988      * @cfg {Boolean} removeMask
8989      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8990      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8991      */
8992     removeMask : false,
8993     /**
8994      * @cfg {String} msg
8995      * The text to display in a centered loading message box (defaults to 'Loading...')
8996      */
8997     msg : 'Loading...',
8998     /**
8999      * @cfg {String} msgCls
9000      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9001      */
9002     msgCls : 'x-mask-loading',
9003
9004     /**
9005      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9006      * @type Boolean
9007      */
9008     disabled: false,
9009
9010     /**
9011      * Disables the mask to prevent it from being displayed
9012      */
9013     disable : function(){
9014        this.disabled = true;
9015     },
9016
9017     /**
9018      * Enables the mask so that it can be displayed
9019      */
9020     enable : function(){
9021         this.disabled = false;
9022     },
9023     
9024     onLoadException : function()
9025     {
9026         Roo.log(arguments);
9027         
9028         if (typeof(arguments[3]) != 'undefined') {
9029             Roo.MessageBox.alert("Error loading",arguments[3]);
9030         } 
9031         /*
9032         try {
9033             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9034                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9035             }   
9036         } catch(e) {
9037             
9038         }
9039         */
9040     
9041         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9042     },
9043     // private
9044     onLoad : function()
9045     {
9046         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9047     },
9048
9049     // private
9050     onBeforeLoad : function(){
9051         if(!this.disabled){
9052             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9053         }
9054     },
9055
9056     // private
9057     destroy : function(){
9058         if(this.store){
9059             this.store.un('beforeload', this.onBeforeLoad, this);
9060             this.store.un('load', this.onLoad, this);
9061             this.store.un('loadexception', this.onLoadException, this);
9062         }else{
9063             var um = this.el.getUpdateManager();
9064             um.un('beforeupdate', this.onBeforeLoad, this);
9065             um.un('update', this.onLoad, this);
9066             um.un('failure', this.onLoad, this);
9067         }
9068     }
9069 };/**
9070  * @class Roo.bootstrap.Table
9071  * @licence LGBL
9072  * @extends Roo.bootstrap.Component
9073  * @children Roo.bootstrap.TableBody
9074  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9075  * Similar to Roo.grid.Grid
9076  * <pre><code>
9077  var table = Roo.factory({
9078     xtype : 'Table',
9079     xns : Roo.bootstrap,
9080     autoSizeColumns: true,
9081     
9082     
9083     store : {
9084         xtype : 'Store',
9085         xns : Roo.data,
9086         remoteSort : true,
9087         sortInfo : { direction : 'ASC', field: 'name' },
9088         proxy : {
9089            xtype : 'HttpProxy',
9090            xns : Roo.data,
9091            method : 'GET',
9092            url : 'https://example.com/some.data.url.json'
9093         },
9094         reader : {
9095            xtype : 'JsonReader',
9096            xns : Roo.data,
9097            fields : [ 'id', 'name', whatever' ],
9098            id : 'id',
9099            root : 'data'
9100         }
9101     },
9102     cm : [
9103         {
9104             xtype : 'ColumnModel',
9105             xns : Roo.grid,
9106             align : 'center',
9107             cursor : 'pointer',
9108             dataIndex : 'is_in_group',
9109             header : "Name",
9110             sortable : true,
9111             renderer : function(v, x , r) {  
9112             
9113                 return String.format("{0}", v)
9114             }
9115             width : 3
9116         } // more columns..
9117     ],
9118     selModel : {
9119         xtype : 'RowSelectionModel',
9120         xns : Roo.bootstrap.Table
9121         // you can add listeners to catch selection change here....
9122     }
9123      
9124
9125  });
9126  // set any options
9127  grid.render(Roo.get("some-div"));
9128 </code></pre>
9129
9130 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9131
9132
9133
9134  *
9135  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9136  * @cfg {Roo.data.Store} store The data store to use
9137  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9138  * 
9139  * @cfg {String} cls table class
9140  *
9141  * 
9142  * @cfg {boolean} striped Should the rows be alternative striped
9143  * @cfg {boolean} bordered Add borders to the table
9144  * @cfg {boolean} hover Add hover highlighting
9145  * @cfg {boolean} condensed Format condensed
9146  * @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,
9147  *                also adds table-responsive (see bootstrap docs for details)
9148  * @cfg {Boolean} loadMask (true|false) default false
9149  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9150  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9151  * @cfg {Boolean} rowSelection (true|false) default false
9152  * @cfg {Boolean} cellSelection (true|false) default false
9153  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
9154  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9155  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9156  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9157  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
9158  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9159  * 
9160  * @constructor
9161  * Create a new Table
9162  * @param {Object} config The config object
9163  */
9164
9165 Roo.bootstrap.Table = function(config)
9166 {
9167     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9168      
9169     // BC...
9170     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9171     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9172     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9173     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9174     
9175     this.view = this; // compat with grid.
9176     
9177     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9178     if (this.sm) {
9179         this.sm.grid = this;
9180         this.selModel = Roo.factory(this.sm, Roo.grid);
9181         this.sm = this.selModel;
9182         this.sm.xmodule = this.xmodule || false;
9183     }
9184     
9185     if (this.cm && typeof(this.cm.config) == 'undefined') {
9186         this.colModel = new Roo.grid.ColumnModel(this.cm);
9187         this.cm = this.colModel;
9188         this.cm.xmodule = this.xmodule || false;
9189     }
9190     if (this.store) {
9191         this.store= Roo.factory(this.store, Roo.data);
9192         this.ds = this.store;
9193         this.ds.xmodule = this.xmodule || false;
9194          
9195     }
9196     if (this.footer && this.store) {
9197         this.footer.dataSource = this.ds;
9198         this.footer = Roo.factory(this.footer);
9199     }
9200     
9201     /** @private */
9202     this.addEvents({
9203         /**
9204          * @event cellclick
9205          * Fires when a cell is clicked
9206          * @param {Roo.bootstrap.Table} this
9207          * @param {Roo.Element} el
9208          * @param {Number} rowIndex
9209          * @param {Number} columnIndex
9210          * @param {Roo.EventObject} e
9211          */
9212         "cellclick" : true,
9213         /**
9214          * @event celldblclick
9215          * Fires when a cell is double clicked
9216          * @param {Roo.bootstrap.Table} this
9217          * @param {Roo.Element} el
9218          * @param {Number} rowIndex
9219          * @param {Number} columnIndex
9220          * @param {Roo.EventObject} e
9221          */
9222         "celldblclick" : true,
9223         /**
9224          * @event rowclick
9225          * Fires when a row is clicked
9226          * @param {Roo.bootstrap.Table} this
9227          * @param {Roo.Element} el
9228          * @param {Number} rowIndex
9229          * @param {Roo.EventObject} e
9230          */
9231         "rowclick" : true,
9232         /**
9233          * @event rowdblclick
9234          * Fires when a row is double clicked
9235          * @param {Roo.bootstrap.Table} this
9236          * @param {Roo.Element} el
9237          * @param {Number} rowIndex
9238          * @param {Roo.EventObject} e
9239          */
9240         "rowdblclick" : true,
9241         /**
9242          * @event mouseover
9243          * Fires when a mouseover occur
9244          * @param {Roo.bootstrap.Table} this
9245          * @param {Roo.Element} el
9246          * @param {Number} rowIndex
9247          * @param {Number} columnIndex
9248          * @param {Roo.EventObject} e
9249          */
9250         "mouseover" : true,
9251         /**
9252          * @event mouseout
9253          * Fires when a mouseout occur
9254          * @param {Roo.bootstrap.Table} this
9255          * @param {Roo.Element} el
9256          * @param {Number} rowIndex
9257          * @param {Number} columnIndex
9258          * @param {Roo.EventObject} e
9259          */
9260         "mouseout" : true,
9261         /**
9262          * @event rowclass
9263          * Fires when a row is rendered, so you can change add a style to it.
9264          * @param {Roo.bootstrap.Table} this
9265          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9266          */
9267         'rowclass' : true,
9268           /**
9269          * @event rowsrendered
9270          * Fires when all the  rows have been rendered
9271          * @param {Roo.bootstrap.Table} this
9272          */
9273         'rowsrendered' : true,
9274         /**
9275          * @event contextmenu
9276          * The raw contextmenu event for the entire grid.
9277          * @param {Roo.EventObject} e
9278          */
9279         "contextmenu" : true,
9280         /**
9281          * @event rowcontextmenu
9282          * Fires when a row is right clicked
9283          * @param {Roo.bootstrap.Table} this
9284          * @param {Number} rowIndex
9285          * @param {Roo.EventObject} e
9286          */
9287         "rowcontextmenu" : true,
9288         /**
9289          * @event cellcontextmenu
9290          * Fires when a cell is right clicked
9291          * @param {Roo.bootstrap.Table} this
9292          * @param {Number} rowIndex
9293          * @param {Number} cellIndex
9294          * @param {Roo.EventObject} e
9295          */
9296          "cellcontextmenu" : true,
9297          /**
9298          * @event headercontextmenu
9299          * Fires when a header is right clicked
9300          * @param {Roo.bootstrap.Table} this
9301          * @param {Number} columnIndex
9302          * @param {Roo.EventObject} e
9303          */
9304         "headercontextmenu" : true,
9305         /**
9306          * @event mousedown
9307          * The raw mousedown event for the entire grid.
9308          * @param {Roo.EventObject} e
9309          */
9310         "mousedown" : true
9311         
9312     });
9313 };
9314
9315 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9316     
9317     cls: false,
9318     
9319     striped : false,
9320     scrollBody : false,
9321     bordered: false,
9322     hover:  false,
9323     condensed : false,
9324     responsive : false,
9325     sm : false,
9326     cm : false,
9327     store : false,
9328     loadMask : false,
9329     footerShow : true,
9330     headerShow : true,
9331     enableColumnResize: true,
9332   
9333     rowSelection : false,
9334     cellSelection : false,
9335     layout : false,
9336
9337     minColumnWidth : 50,
9338     
9339     // Roo.Element - the tbody
9340     bodyEl: false,  // <tbody> Roo.Element - thead element    
9341     headEl: false,  // <thead> Roo.Element - thead element
9342     resizeProxy : false, // proxy element for dragging?
9343
9344
9345     
9346     container: false, // used by gridpanel...
9347     
9348     lazyLoad : false,
9349     
9350     CSS : Roo.util.CSS,
9351     
9352     auto_hide_footer : false,
9353     
9354     view: false, // actually points to this..
9355     
9356     getAutoCreate : function()
9357     {
9358         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9359         
9360         cfg = {
9361             tag: 'table',
9362             cls : 'table', 
9363             cn : []
9364         };
9365         // this get's auto added by panel.Grid
9366         if (this.scrollBody) {
9367             cfg.cls += ' table-body-fixed';
9368         }    
9369         if (this.striped) {
9370             cfg.cls += ' table-striped';
9371         }
9372         
9373         if (this.hover) {
9374             cfg.cls += ' table-hover';
9375         }
9376         if (this.bordered) {
9377             cfg.cls += ' table-bordered';
9378         }
9379         if (this.condensed) {
9380             cfg.cls += ' table-condensed';
9381         }
9382         
9383         if (this.responsive) {
9384             cfg.cls += ' table-responsive';
9385         }
9386         
9387         if (this.cls) {
9388             cfg.cls+=  ' ' +this.cls;
9389         }
9390         
9391         
9392         
9393         if (this.layout) {
9394             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9395         }
9396         
9397         if(this.store || this.cm){
9398             if(this.headerShow){
9399                 cfg.cn.push(this.renderHeader());
9400             }
9401             
9402             cfg.cn.push(this.renderBody());
9403             
9404             if(this.footerShow){
9405                 cfg.cn.push(this.renderFooter());
9406             }
9407             // where does this come from?
9408             //cfg.cls+=  ' TableGrid';
9409         }
9410         
9411         return { cn : [ cfg ] };
9412     },
9413     
9414     initEvents : function()
9415     {   
9416         if(!this.store || !this.cm){
9417             return;
9418         }
9419         if (this.selModel) {
9420             this.selModel.initEvents();
9421         }
9422         
9423         
9424         //Roo.log('initEvents with ds!!!!');
9425         
9426         this.bodyEl = this.el.select('tbody', true).first();
9427         this.headEl = this.el.select('thead', true).first();
9428         this.mainFoot = this.el.select('tfoot', true).first();
9429         
9430         
9431         
9432         
9433         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9434             e.on('click', this.sort, this);
9435         }, this);
9436         
9437         
9438         // why is this done????? = it breaks dialogs??
9439         //this.parent().el.setStyle('position', 'relative');
9440         
9441         
9442         if (this.footer) {
9443             this.footer.parentId = this.id;
9444             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9445             
9446             if(this.lazyLoad){
9447                 this.el.select('tfoot tr td').first().addClass('hide');
9448             }
9449         } 
9450         
9451         if(this.loadMask) {
9452             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9453         }
9454         
9455         this.store.on('load', this.onLoad, this);
9456         this.store.on('beforeload', this.onBeforeLoad, this);
9457         this.store.on('update', this.onUpdate, this);
9458         this.store.on('add', this.onAdd, this);
9459         this.store.on("clear", this.clear, this);
9460         
9461         this.el.on("contextmenu", this.onContextMenu, this);
9462         
9463         
9464         this.cm.on("headerchange", this.onHeaderChange, this);
9465         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9466
9467  //?? does bodyEl get replaced on render?
9468         this.bodyEl.on("click", this.onClick, this);
9469         this.bodyEl.on("dblclick", this.onDblClick, this);        
9470         this.bodyEl.on('scroll', this.onBodyScroll, this);
9471
9472         // guessing mainbody will work - this relays usually caught by selmodel at present.
9473         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9474   
9475   
9476         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9477         
9478   
9479         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9480             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9481         }
9482         
9483         this.initCSS();
9484     },
9485     // Compatibility with grid - we implement all the view features at present.
9486     getView : function()
9487     {
9488         return this;
9489     },
9490     
9491     initCSS : function()
9492     {
9493         
9494         
9495         var cm = this.cm, styles = [];
9496         this.CSS.removeStyleSheet(this.id + '-cssrules');
9497         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9498         // we can honour xs/sm/md/xl  as widths...
9499         // we first have to decide what widht we are currently at...
9500         var sz = Roo.getGridSize();
9501         
9502         var total = 0;
9503         var last = -1;
9504         var cols = []; // visable cols.
9505         var total_abs = 0;
9506         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9507             var w = cm.getColumnWidth(i, false);
9508             if(cm.isHidden(i)){
9509                 cols.push( { rel : false, abs : 0 });
9510                 continue;
9511             }
9512             if (w !== false) {
9513                 cols.push( { rel : false, abs : w });
9514                 total_abs += w;
9515                 last = i; // not really..
9516                 continue;
9517             }
9518             var w = cm.getColumnWidth(i, sz);
9519             if (w > 0) {
9520                 last = i
9521             }
9522             total += w;
9523             cols.push( { rel : w, abs : false });
9524         }
9525         
9526         var avail = this.bodyEl.dom.clientWidth - total_abs;
9527         
9528         var unitWidth = Math.floor(avail / total);
9529         var rem = avail - (unitWidth * total);
9530         
9531         var hidden, width, pos = 0 , splithide , left;
9532         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9533             
9534             hidden = 'display:none;';
9535             left = '';
9536             width  = 'width:0px;';
9537             splithide = '';
9538             if(!cm.isHidden(i)){
9539                 hidden = '';
9540                 
9541                 
9542                 // we can honour xs/sm/md/xl ?
9543                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9544                 if (w===0) {
9545                     hidden = 'display:none;';
9546                 }
9547                 // width should return a small number...
9548                 if (i == last) {
9549                     w+=rem; // add the remaining with..
9550                 }
9551                 pos += w;
9552                 left = "left:" + (pos -4) + "px;";
9553                 width = "width:" + w+ "px;";
9554                 
9555             }
9556             if (this.responsive) {
9557                 width = '';
9558                 left = '';
9559                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9560                 splithide = 'display: none;';
9561             }
9562             
9563             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9564             if (this.headEl) {
9565                 if (i == last) {
9566                     splithide = 'display:none;';
9567                 }
9568                 
9569                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9570                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9571                 );
9572             }
9573             
9574         }
9575         //Roo.log(styles.join(''));
9576         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9577         
9578     },
9579     
9580     
9581     
9582     onContextMenu : function(e, t)
9583     {
9584         this.processEvent("contextmenu", e);
9585     },
9586     
9587     processEvent : function(name, e)
9588     {
9589         if (name != 'touchstart' ) {
9590             this.fireEvent(name, e);    
9591         }
9592         
9593         var t = e.getTarget();
9594         
9595         var cell = Roo.get(t);
9596         
9597         if(!cell){
9598             return;
9599         }
9600         
9601         if(cell.findParent('tfoot', false, true)){
9602             return;
9603         }
9604         
9605         if(cell.findParent('thead', false, true)){
9606             
9607             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9608                 cell = Roo.get(t).findParent('th', false, true);
9609                 if (!cell) {
9610                     Roo.log("failed to find th in thead?");
9611                     Roo.log(e.getTarget());
9612                     return;
9613                 }
9614             }
9615             
9616             var cellIndex = cell.dom.cellIndex;
9617             
9618             var ename = name == 'touchstart' ? 'click' : name;
9619             this.fireEvent("header" + ename, this, cellIndex, e);
9620             
9621             return;
9622         }
9623         
9624         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9625             cell = Roo.get(t).findParent('td', false, true);
9626             if (!cell) {
9627                 Roo.log("failed to find th in tbody?");
9628                 Roo.log(e.getTarget());
9629                 return;
9630             }
9631         }
9632         
9633         var row = cell.findParent('tr', false, true);
9634         var cellIndex = cell.dom.cellIndex;
9635         var rowIndex = row.dom.rowIndex - 1;
9636         
9637         if(row !== false){
9638             
9639             this.fireEvent("row" + name, this, rowIndex, e);
9640             
9641             if(cell !== false){
9642             
9643                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9644             }
9645         }
9646         
9647     },
9648     
9649     onMouseover : function(e, el)
9650     {
9651         var cell = Roo.get(el);
9652         
9653         if(!cell){
9654             return;
9655         }
9656         
9657         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9658             cell = cell.findParent('td', false, true);
9659         }
9660         
9661         var row = cell.findParent('tr', false, true);
9662         var cellIndex = cell.dom.cellIndex;
9663         var rowIndex = row.dom.rowIndex - 1; // start from 0
9664         
9665         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9666         
9667     },
9668     
9669     onMouseout : function(e, el)
9670     {
9671         var cell = Roo.get(el);
9672         
9673         if(!cell){
9674             return;
9675         }
9676         
9677         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9678             cell = cell.findParent('td', false, true);
9679         }
9680         
9681         var row = cell.findParent('tr', false, true);
9682         var cellIndex = cell.dom.cellIndex;
9683         var rowIndex = row.dom.rowIndex - 1; // start from 0
9684         
9685         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9686         
9687     },
9688     
9689     onClick : function(e, el)
9690     {
9691         var cell = Roo.get(el);
9692         
9693         if(!cell || (!this.cellSelection && !this.rowSelection)){
9694             return;
9695         }
9696         
9697         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9698             cell = cell.findParent('td', false, true);
9699         }
9700         
9701         if(!cell || typeof(cell) == 'undefined'){
9702             return;
9703         }
9704         
9705         var row = cell.findParent('tr', false, true);
9706         
9707         if(!row || typeof(row) == 'undefined'){
9708             return;
9709         }
9710         
9711         var cellIndex = cell.dom.cellIndex;
9712         var rowIndex = this.getRowIndex(row);
9713         
9714         // why??? - should these not be based on SelectionModel?
9715         //if(this.cellSelection){
9716             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9717         //}
9718         
9719         //if(this.rowSelection){
9720             this.fireEvent('rowclick', this, row, rowIndex, e);
9721         //}
9722          
9723     },
9724         
9725     onDblClick : function(e,el)
9726     {
9727         var cell = Roo.get(el);
9728         
9729         if(!cell || (!this.cellSelection && !this.rowSelection)){
9730             return;
9731         }
9732         
9733         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9734             cell = cell.findParent('td', false, true);
9735         }
9736         
9737         if(!cell || typeof(cell) == 'undefined'){
9738             return;
9739         }
9740         
9741         var row = cell.findParent('tr', false, true);
9742         
9743         if(!row || typeof(row) == 'undefined'){
9744             return;
9745         }
9746         
9747         var cellIndex = cell.dom.cellIndex;
9748         var rowIndex = this.getRowIndex(row);
9749         
9750         if(this.cellSelection){
9751             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9752         }
9753         
9754         if(this.rowSelection){
9755             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9756         }
9757     },
9758     findRowIndex : function(el)
9759     {
9760         var cell = Roo.get(el);
9761         if(!cell) {
9762             return false;
9763         }
9764         var row = cell.findParent('tr', false, true);
9765         
9766         if(!row || typeof(row) == 'undefined'){
9767             return false;
9768         }
9769         return this.getRowIndex(row);
9770     },
9771     sort : function(e,el)
9772     {
9773         var col = Roo.get(el);
9774         
9775         if(!col.hasClass('sortable')){
9776             return;
9777         }
9778         
9779         var sort = col.attr('sort');
9780         var dir = 'ASC';
9781         
9782         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9783             dir = 'DESC';
9784         }
9785         
9786         this.store.sortInfo = {field : sort, direction : dir};
9787         
9788         if (this.footer) {
9789             Roo.log("calling footer first");
9790             this.footer.onClick('first');
9791         } else {
9792         
9793             this.store.load({ params : { start : 0 } });
9794         }
9795     },
9796     
9797     renderHeader : function()
9798     {
9799         var header = {
9800             tag: 'thead',
9801             cn : []
9802         };
9803         
9804         var cm = this.cm;
9805         this.totalWidth = 0;
9806         
9807         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9808             
9809             var config = cm.config[i];
9810             
9811             var c = {
9812                 tag: 'th',
9813                 cls : 'x-hcol-' + i,
9814                 style : '',
9815                 
9816                 html: cm.getColumnHeader(i)
9817             };
9818             
9819             var tooltip = cm.getColumnTooltip(i);
9820             if (tooltip) {
9821                 c.tooltip = tooltip;
9822             }
9823             
9824             
9825             var hh = '';
9826             
9827             if(typeof(config.sortable) != 'undefined' && config.sortable){
9828                 c.cls += ' sortable';
9829                 c.html = '<i class="fa"></i>' + c.html;
9830             }
9831             
9832             // could use BS4 hidden-..-down 
9833             
9834             if(typeof(config.lgHeader) != 'undefined'){
9835                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9836             }
9837             
9838             if(typeof(config.mdHeader) != 'undefined'){
9839                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9840             }
9841             
9842             if(typeof(config.smHeader) != 'undefined'){
9843                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9844             }
9845             
9846             if(typeof(config.xsHeader) != 'undefined'){
9847                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9848             }
9849             
9850             if(hh.length){
9851                 c.html = hh;
9852             }
9853             
9854             if(typeof(config.tooltip) != 'undefined'){
9855                 c.tooltip = config.tooltip;
9856             }
9857             
9858             if(typeof(config.colspan) != 'undefined'){
9859                 c.colspan = config.colspan;
9860             }
9861             
9862             // hidden is handled by CSS now
9863             
9864             if(typeof(config.dataIndex) != 'undefined'){
9865                 c.sort = config.dataIndex;
9866             }
9867             
9868            
9869             
9870             if(typeof(config.align) != 'undefined' && config.align.length){
9871                 c.style += ' text-align:' + config.align + ';';
9872             }
9873             
9874             /* width is done in CSS
9875              *if(typeof(config.width) != 'undefined'){
9876                 c.style += ' width:' + config.width + 'px;';
9877                 this.totalWidth += config.width;
9878             } else {
9879                 this.totalWidth += 100; // assume minimum of 100 per column?
9880             }
9881             */
9882             
9883             if(typeof(config.cls) != 'undefined'){
9884                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9885             }
9886             // this is the bit that doesnt reall work at all...
9887             
9888             if (this.responsive) {
9889                  
9890             
9891                 ['xs','sm','md','lg'].map(function(size){
9892                     
9893                     if(typeof(config[size]) == 'undefined'){
9894                         return;
9895                     }
9896                      
9897                     if (!config[size]) { // 0 = hidden
9898                         // BS 4 '0' is treated as hide that column and below.
9899                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9900                         return;
9901                     }
9902                     
9903                     c.cls += ' col-' + size + '-' + config[size] + (
9904                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9905                     );
9906                     
9907                     
9908                 });
9909             }
9910             // at the end?
9911             
9912             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9913             
9914             
9915             
9916             
9917             header.cn.push(c)
9918         }
9919         
9920         return header;
9921     },
9922     
9923     renderBody : function()
9924     {
9925         var body = {
9926             tag: 'tbody',
9927             cn : [
9928                 {
9929                     tag: 'tr',
9930                     cn : [
9931                         {
9932                             tag : 'td',
9933                             colspan :  this.cm.getColumnCount()
9934                         }
9935                     ]
9936                 }
9937             ]
9938         };
9939         
9940         return body;
9941     },
9942     
9943     renderFooter : function()
9944     {
9945         var footer = {
9946             tag: 'tfoot',
9947             cn : [
9948                 {
9949                     tag: 'tr',
9950                     cn : [
9951                         {
9952                             tag : 'td',
9953                             colspan :  this.cm.getColumnCount()
9954                         }
9955                     ]
9956                 }
9957             ]
9958         };
9959         
9960         return footer;
9961     },
9962     
9963     
9964     
9965     onLoad : function()
9966     {
9967 //        Roo.log('ds onload');
9968         this.clear();
9969         
9970         var _this = this;
9971         var cm = this.cm;
9972         var ds = this.store;
9973         
9974         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9975             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9976             if (_this.store.sortInfo) {
9977                     
9978                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9979                     e.select('i', true).addClass(['fa-arrow-up']);
9980                 }
9981                 
9982                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9983                     e.select('i', true).addClass(['fa-arrow-down']);
9984                 }
9985             }
9986         });
9987         
9988         var tbody =  this.bodyEl;
9989               
9990         if(ds.getCount() > 0){
9991             ds.data.each(function(d,rowIndex){
9992                 var row =  this.renderRow(cm, ds, rowIndex);
9993                 
9994                 tbody.createChild(row);
9995                 
9996                 var _this = this;
9997                 
9998                 if(row.cellObjects.length){
9999                     Roo.each(row.cellObjects, function(r){
10000                         _this.renderCellObject(r);
10001                     })
10002                 }
10003                 
10004             }, this);
10005         }
10006         
10007         var tfoot = this.el.select('tfoot', true).first();
10008         
10009         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10010             
10011             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10012             
10013             var total = this.ds.getTotalCount();
10014             
10015             if(this.footer.pageSize < total){
10016                 this.mainFoot.show();
10017             }
10018         }
10019         
10020         Roo.each(this.el.select('tbody td', true).elements, function(e){
10021             e.on('mouseover', _this.onMouseover, _this);
10022         });
10023         
10024         Roo.each(this.el.select('tbody td', true).elements, function(e){
10025             e.on('mouseout', _this.onMouseout, _this);
10026         });
10027         this.fireEvent('rowsrendered', this);
10028         
10029         this.autoSize();
10030         
10031         this.initCSS(); /// resize cols
10032
10033         
10034     },
10035     
10036     
10037     onUpdate : function(ds,record)
10038     {
10039         this.refreshRow(record);
10040         this.autoSize();
10041     },
10042     
10043     onRemove : function(ds, record, index, isUpdate){
10044         if(isUpdate !== true){
10045             this.fireEvent("beforerowremoved", this, index, record);
10046         }
10047         var bt = this.bodyEl.dom;
10048         
10049         var rows = this.el.select('tbody > tr', true).elements;
10050         
10051         if(typeof(rows[index]) != 'undefined'){
10052             bt.removeChild(rows[index].dom);
10053         }
10054         
10055 //        if(bt.rows[index]){
10056 //            bt.removeChild(bt.rows[index]);
10057 //        }
10058         
10059         if(isUpdate !== true){
10060             //this.stripeRows(index);
10061             //this.syncRowHeights(index, index);
10062             //this.layout();
10063             this.fireEvent("rowremoved", this, index, record);
10064         }
10065     },
10066     
10067     onAdd : function(ds, records, rowIndex)
10068     {
10069         //Roo.log('on Add called');
10070         // - note this does not handle multiple adding very well..
10071         var bt = this.bodyEl.dom;
10072         for (var i =0 ; i < records.length;i++) {
10073             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10074             //Roo.log(records[i]);
10075             //Roo.log(this.store.getAt(rowIndex+i));
10076             this.insertRow(this.store, rowIndex + i, false);
10077             return;
10078         }
10079         
10080     },
10081     
10082     
10083     refreshRow : function(record){
10084         var ds = this.store, index;
10085         if(typeof record == 'number'){
10086             index = record;
10087             record = ds.getAt(index);
10088         }else{
10089             index = ds.indexOf(record);
10090             if (index < 0) {
10091                 return; // should not happen - but seems to 
10092             }
10093         }
10094         this.insertRow(ds, index, true);
10095         this.autoSize();
10096         this.onRemove(ds, record, index+1, true);
10097         this.autoSize();
10098         //this.syncRowHeights(index, index);
10099         //this.layout();
10100         this.fireEvent("rowupdated", this, index, record);
10101     },
10102     // private - called by RowSelection
10103     onRowSelect : function(rowIndex){
10104         var row = this.getRowDom(rowIndex);
10105         row.addClass(['bg-info','info']);
10106     },
10107     // private - called by RowSelection
10108     onRowDeselect : function(rowIndex)
10109     {
10110         if (rowIndex < 0) {
10111             return;
10112         }
10113         var row = this.getRowDom(rowIndex);
10114         row.removeClass(['bg-info','info']);
10115     },
10116       /**
10117      * Focuses the specified row.
10118      * @param {Number} row The row index
10119      */
10120     focusRow : function(row)
10121     {
10122         //Roo.log('GridView.focusRow');
10123         var x = this.bodyEl.dom.scrollLeft;
10124         this.focusCell(row, 0, false);
10125         this.bodyEl.dom.scrollLeft = x;
10126
10127     },
10128      /**
10129      * Focuses the specified cell.
10130      * @param {Number} row The row index
10131      * @param {Number} col The column index
10132      * @param {Boolean} hscroll false to disable horizontal scrolling
10133      */
10134     focusCell : function(row, col, hscroll)
10135     {
10136         //Roo.log('GridView.focusCell');
10137         var el = this.ensureVisible(row, col, hscroll);
10138         // not sure what focusEL achives = it's a <a> pos relative 
10139         //this.focusEl.alignTo(el, "tl-tl");
10140         //if(Roo.isGecko){
10141         //    this.focusEl.focus();
10142         //}else{
10143         //    this.focusEl.focus.defer(1, this.focusEl);
10144         //}
10145     },
10146     
10147      /**
10148      * Scrolls the specified cell into view
10149      * @param {Number} row The row index
10150      * @param {Number} col The column index
10151      * @param {Boolean} hscroll false to disable horizontal scrolling
10152      */
10153     ensureVisible : function(row, col, hscroll)
10154     {
10155         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10156         //return null; //disable for testing.
10157         if(typeof row != "number"){
10158             row = row.rowIndex;
10159         }
10160         if(row < 0 && row >= this.ds.getCount()){
10161             return  null;
10162         }
10163         col = (col !== undefined ? col : 0);
10164         var cm = this.cm;
10165         while(cm.isHidden(col)){
10166             col++;
10167         }
10168
10169         var el = this.getCellDom(row, col);
10170         if(!el){
10171             return null;
10172         }
10173         var c = this.bodyEl.dom;
10174
10175         var ctop = parseInt(el.offsetTop, 10);
10176         var cleft = parseInt(el.offsetLeft, 10);
10177         var cbot = ctop + el.offsetHeight;
10178         var cright = cleft + el.offsetWidth;
10179
10180         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10181         var ch = 0; //?? header is not withing the area?
10182         var stop = parseInt(c.scrollTop, 10);
10183         var sleft = parseInt(c.scrollLeft, 10);
10184         var sbot = stop + ch;
10185         var sright = sleft + c.clientWidth;
10186         /*
10187         Roo.log('GridView.ensureVisible:' +
10188                 ' ctop:' + ctop +
10189                 ' c.clientHeight:' + c.clientHeight +
10190                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10191                 ' stop:' + stop +
10192                 ' cbot:' + cbot +
10193                 ' sbot:' + sbot +
10194                 ' ch:' + ch  
10195                 );
10196         */
10197         if(ctop < stop){
10198             c.scrollTop = ctop;
10199             //Roo.log("set scrolltop to ctop DISABLE?");
10200         }else if(cbot > sbot){
10201             //Roo.log("set scrolltop to cbot-ch");
10202             c.scrollTop = cbot-ch;
10203         }
10204
10205         if(hscroll !== false){
10206             if(cleft < sleft){
10207                 c.scrollLeft = cleft;
10208             }else if(cright > sright){
10209                 c.scrollLeft = cright-c.clientWidth;
10210             }
10211         }
10212
10213         return el;
10214     },
10215     
10216     
10217     insertRow : function(dm, rowIndex, isUpdate){
10218         
10219         if(!isUpdate){
10220             this.fireEvent("beforerowsinserted", this, rowIndex);
10221         }
10222             //var s = this.getScrollState();
10223         var row = this.renderRow(this.cm, this.store, rowIndex);
10224         // insert before rowIndex..
10225         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10226         
10227         var _this = this;
10228                 
10229         if(row.cellObjects.length){
10230             Roo.each(row.cellObjects, function(r){
10231                 _this.renderCellObject(r);
10232             })
10233         }
10234             
10235         if(!isUpdate){
10236             this.fireEvent("rowsinserted", this, rowIndex);
10237             //this.syncRowHeights(firstRow, lastRow);
10238             //this.stripeRows(firstRow);
10239             //this.layout();
10240         }
10241         
10242     },
10243     
10244     
10245     getRowDom : function(rowIndex)
10246     {
10247         var rows = this.el.select('tbody > tr', true).elements;
10248         
10249         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10250         
10251     },
10252     getCellDom : function(rowIndex, colIndex)
10253     {
10254         var row = this.getRowDom(rowIndex);
10255         if (row === false) {
10256             return false;
10257         }
10258         var cols = row.select('td', true).elements;
10259         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10260         
10261     },
10262     
10263     // returns the object tree for a tr..
10264   
10265     
10266     renderRow : function(cm, ds, rowIndex) 
10267     {
10268         var d = ds.getAt(rowIndex);
10269         
10270         var row = {
10271             tag : 'tr',
10272             cls : 'x-row-' + rowIndex,
10273             cn : []
10274         };
10275             
10276         var cellObjects = [];
10277         
10278         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10279             var config = cm.config[i];
10280             
10281             var renderer = cm.getRenderer(i);
10282             var value = '';
10283             var id = false;
10284             
10285             if(typeof(renderer) !== 'undefined'){
10286                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10287             }
10288             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10289             // and are rendered into the cells after the row is rendered - using the id for the element.
10290             
10291             if(typeof(value) === 'object'){
10292                 id = Roo.id();
10293                 cellObjects.push({
10294                     container : id,
10295                     cfg : value 
10296                 })
10297             }
10298             
10299             var rowcfg = {
10300                 record: d,
10301                 rowIndex : rowIndex,
10302                 colIndex : i,
10303                 rowClass : ''
10304             };
10305
10306             this.fireEvent('rowclass', this, rowcfg);
10307             
10308             var td = {
10309                 tag: 'td',
10310                 // this might end up displaying HTML?
10311                 // this is too messy... - better to only do it on columsn you know are going to be too long
10312                 //tooltip : (typeof(value) === 'object') ? '' : value,
10313                 cls : rowcfg.rowClass + ' x-col-' + i,
10314                 style: '',
10315                 html: (typeof(value) === 'object') ? '' : value
10316             };
10317             
10318             if (id) {
10319                 td.id = id;
10320             }
10321             
10322             if(typeof(config.colspan) != 'undefined'){
10323                 td.colspan = config.colspan;
10324             }
10325             
10326             
10327             
10328             if(typeof(config.align) != 'undefined' && config.align.length){
10329                 td.style += ' text-align:' + config.align + ';';
10330             }
10331             if(typeof(config.valign) != 'undefined' && config.valign.length){
10332                 td.style += ' vertical-align:' + config.valign + ';';
10333             }
10334             /*
10335             if(typeof(config.width) != 'undefined'){
10336                 td.style += ' width:' +  config.width + 'px;';
10337             }
10338             */
10339             
10340             if(typeof(config.cursor) != 'undefined'){
10341                 td.style += ' cursor:' +  config.cursor + ';';
10342             }
10343             
10344             if(typeof(config.cls) != 'undefined'){
10345                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10346             }
10347             if (this.responsive) {
10348                 ['xs','sm','md','lg'].map(function(size){
10349                     
10350                     if(typeof(config[size]) == 'undefined'){
10351                         return;
10352                     }
10353                     
10354                     
10355                       
10356                     if (!config[size]) { // 0 = hidden
10357                         // BS 4 '0' is treated as hide that column and below.
10358                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10359                         return;
10360                     }
10361                     
10362                     td.cls += ' col-' + size + '-' + config[size] + (
10363                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10364                     );
10365                      
10366     
10367                 });
10368             }
10369             row.cn.push(td);
10370            
10371         }
10372         
10373         row.cellObjects = cellObjects;
10374         
10375         return row;
10376           
10377     },
10378     
10379     
10380     
10381     onBeforeLoad : function()
10382     {
10383         
10384     },
10385      /**
10386      * Remove all rows
10387      */
10388     clear : function()
10389     {
10390         this.el.select('tbody', true).first().dom.innerHTML = '';
10391     },
10392     /**
10393      * Show or hide a row.
10394      * @param {Number} rowIndex to show or hide
10395      * @param {Boolean} state hide
10396      */
10397     setRowVisibility : function(rowIndex, state)
10398     {
10399         var bt = this.bodyEl.dom;
10400         
10401         var rows = this.el.select('tbody > tr', true).elements;
10402         
10403         if(typeof(rows[rowIndex]) == 'undefined'){
10404             return;
10405         }
10406         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10407         
10408     },
10409     
10410     
10411     getSelectionModel : function(){
10412         if(!this.selModel){
10413             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10414         }
10415         return this.selModel;
10416     },
10417     /*
10418      * Render the Roo.bootstrap object from renderder
10419      */
10420     renderCellObject : function(r)
10421     {
10422         var _this = this;
10423         
10424         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10425         
10426         var t = r.cfg.render(r.container);
10427         
10428         if(r.cfg.cn){
10429             Roo.each(r.cfg.cn, function(c){
10430                 var child = {
10431                     container: t.getChildContainer(),
10432                     cfg: c
10433                 };
10434                 _this.renderCellObject(child);
10435             })
10436         }
10437     },
10438     /**
10439      * get the Row Index from a dom element.
10440      * @param {Roo.Element} row The row to look for
10441      * @returns {Number} the row
10442      */
10443     getRowIndex : function(row)
10444     {
10445         var rowIndex = -1;
10446         
10447         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10448             if(el != row){
10449                 return;
10450             }
10451             
10452             rowIndex = index;
10453         });
10454         
10455         return rowIndex;
10456     },
10457     /**
10458      * get the header TH element for columnIndex
10459      * @param {Number} columnIndex
10460      * @returns {Roo.Element}
10461      */
10462     getHeaderIndex: function(colIndex)
10463     {
10464         var cols = this.headEl.select('th', true).elements;
10465         return cols[colIndex]; 
10466     },
10467     /**
10468      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10469      * @param {domElement} cell to look for
10470      * @returns {Number} the column
10471      */
10472     getCellIndex : function(cell)
10473     {
10474         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10475         if(id){
10476             return parseInt(id[1], 10);
10477         }
10478         return 0;
10479     },
10480      /**
10481      * Returns the grid's underlying element = used by panel.Grid
10482      * @return {Element} The element
10483      */
10484     getGridEl : function(){
10485         return this.el;
10486     },
10487      /**
10488      * Forces a resize - used by panel.Grid
10489      * @return {Element} The element
10490      */
10491     autoSize : function()
10492     {
10493         //var ctr = Roo.get(this.container.dom.parentElement);
10494         var ctr = Roo.get(this.el.dom);
10495         
10496         var thd = this.getGridEl().select('thead',true).first();
10497         var tbd = this.getGridEl().select('tbody', true).first();
10498         var tfd = this.getGridEl().select('tfoot', true).first();
10499         
10500         var cw = ctr.getWidth();
10501         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10502         
10503         if (tbd) {
10504             
10505             tbd.setWidth(ctr.getWidth());
10506             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10507             // this needs fixing for various usage - currently only hydra job advers I think..
10508             //tdb.setHeight(
10509             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10510             //); 
10511             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10512             cw -= barsize;
10513         }
10514         cw = Math.max(cw, this.totalWidth);
10515         this.getGridEl().select('tbody tr',true).setWidth(cw);
10516         this.initCSS();
10517         
10518         // resize 'expandable coloumn?
10519         
10520         return; // we doe not have a view in this design..
10521         
10522     },
10523     onBodyScroll: function()
10524     {
10525         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10526         if(this.headEl){
10527             this.headEl.setStyle({
10528                 'position' : 'relative',
10529                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10530             });
10531         }
10532         
10533         if(this.lazyLoad){
10534             
10535             var scrollHeight = this.bodyEl.dom.scrollHeight;
10536             
10537             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10538             
10539             var height = this.bodyEl.getHeight();
10540             
10541             if(scrollHeight - height == scrollTop) {
10542                 
10543                 var total = this.ds.getTotalCount();
10544                 
10545                 if(this.footer.cursor + this.footer.pageSize < total){
10546                     
10547                     this.footer.ds.load({
10548                         params : {
10549                             start : this.footer.cursor + this.footer.pageSize,
10550                             limit : this.footer.pageSize
10551                         },
10552                         add : true
10553                     });
10554                 }
10555             }
10556             
10557         }
10558     },
10559     onColumnSplitterMoved : function(i, diff)
10560     {
10561         this.userResized = true;
10562         
10563         var cm = this.colModel;
10564         
10565         var w = this.getHeaderIndex(i).getWidth() + diff;
10566         
10567         
10568         cm.setColumnWidth(i, w, true);
10569         this.initCSS();
10570         //var cid = cm.getColumnId(i); << not used in this version?
10571        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10572         
10573         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10574         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10575         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10576 */
10577         //this.updateSplitters();
10578         //this.layout(); << ??
10579         this.fireEvent("columnresize", i, w);
10580     },
10581     onHeaderChange : function()
10582     {
10583         var header = this.renderHeader();
10584         var table = this.el.select('table', true).first();
10585         
10586         this.headEl.remove();
10587         this.headEl = table.createChild(header, this.bodyEl, false);
10588         
10589         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10590             e.on('click', this.sort, this);
10591         }, this);
10592         
10593         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10594             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10595         }
10596         
10597     },
10598     
10599     onHiddenChange : function(colModel, colIndex, hidden)
10600     {
10601         /*
10602         this.cm.setHidden()
10603         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10604         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10605         
10606         this.CSS.updateRule(thSelector, "display", "");
10607         this.CSS.updateRule(tdSelector, "display", "");
10608         
10609         if(hidden){
10610             this.CSS.updateRule(thSelector, "display", "none");
10611             this.CSS.updateRule(tdSelector, "display", "none");
10612         }
10613         */
10614         // onload calls initCSS()
10615         this.onHeaderChange();
10616         this.onLoad();
10617     },
10618     
10619     setColumnWidth: function(col_index, width)
10620     {
10621         // width = "md-2 xs-2..."
10622         if(!this.colModel.config[col_index]) {
10623             return;
10624         }
10625         
10626         var w = width.split(" ");
10627         
10628         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10629         
10630         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10631         
10632         
10633         for(var j = 0; j < w.length; j++) {
10634             
10635             if(!w[j]) {
10636                 continue;
10637             }
10638             
10639             var size_cls = w[j].split("-");
10640             
10641             if(!Number.isInteger(size_cls[1] * 1)) {
10642                 continue;
10643             }
10644             
10645             if(!this.colModel.config[col_index][size_cls[0]]) {
10646                 continue;
10647             }
10648             
10649             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10650                 continue;
10651             }
10652             
10653             h_row[0].classList.replace(
10654                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10655                 "col-"+size_cls[0]+"-"+size_cls[1]
10656             );
10657             
10658             for(var i = 0; i < rows.length; i++) {
10659                 
10660                 var size_cls = w[j].split("-");
10661                 
10662                 if(!Number.isInteger(size_cls[1] * 1)) {
10663                     continue;
10664                 }
10665                 
10666                 if(!this.colModel.config[col_index][size_cls[0]]) {
10667                     continue;
10668                 }
10669                 
10670                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10671                     continue;
10672                 }
10673                 
10674                 rows[i].classList.replace(
10675                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10676                     "col-"+size_cls[0]+"-"+size_cls[1]
10677                 );
10678             }
10679             
10680             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10681         }
10682     }
10683 });
10684
10685 // currently only used to find the split on drag.. 
10686 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10687
10688 /**
10689  * @depricated
10690 */
10691 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10692 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10693 /*
10694  * - LGPL
10695  *
10696  * table cell
10697  * 
10698  */
10699
10700 /**
10701  * @class Roo.bootstrap.TableCell
10702  * @extends Roo.bootstrap.Component
10703  * @children Roo.bootstrap.Component
10704  * @parent Roo.bootstrap.TableRow
10705  * Bootstrap TableCell class
10706  * 
10707  * @cfg {String} html cell contain text
10708  * @cfg {String} cls cell class
10709  * @cfg {String} tag cell tag (td|th) default td
10710  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10711  * @cfg {String} align Aligns the content in a cell
10712  * @cfg {String} axis Categorizes cells
10713  * @cfg {String} bgcolor Specifies the background color of a cell
10714  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10715  * @cfg {Number} colspan Specifies the number of columns a cell should span
10716  * @cfg {String} headers Specifies one or more header cells a cell is related to
10717  * @cfg {Number} height Sets the height of a cell
10718  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10719  * @cfg {Number} rowspan Sets the number of rows a cell should span
10720  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10721  * @cfg {String} valign Vertical aligns the content in a cell
10722  * @cfg {Number} width Specifies the width of a cell
10723  * 
10724  * @constructor
10725  * Create a new TableCell
10726  * @param {Object} config The config object
10727  */
10728
10729 Roo.bootstrap.TableCell = function(config){
10730     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10731 };
10732
10733 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10734     
10735     html: false,
10736     cls: false,
10737     tag: false,
10738     abbr: false,
10739     align: false,
10740     axis: false,
10741     bgcolor: false,
10742     charoff: false,
10743     colspan: false,
10744     headers: false,
10745     height: false,
10746     nowrap: false,
10747     rowspan: false,
10748     scope: false,
10749     valign: false,
10750     width: false,
10751     
10752     
10753     getAutoCreate : function(){
10754         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10755         
10756         cfg = {
10757             tag: 'td'
10758         };
10759         
10760         if(this.tag){
10761             cfg.tag = this.tag;
10762         }
10763         
10764         if (this.html) {
10765             cfg.html=this.html
10766         }
10767         if (this.cls) {
10768             cfg.cls=this.cls
10769         }
10770         if (this.abbr) {
10771             cfg.abbr=this.abbr
10772         }
10773         if (this.align) {
10774             cfg.align=this.align
10775         }
10776         if (this.axis) {
10777             cfg.axis=this.axis
10778         }
10779         if (this.bgcolor) {
10780             cfg.bgcolor=this.bgcolor
10781         }
10782         if (this.charoff) {
10783             cfg.charoff=this.charoff
10784         }
10785         if (this.colspan) {
10786             cfg.colspan=this.colspan
10787         }
10788         if (this.headers) {
10789             cfg.headers=this.headers
10790         }
10791         if (this.height) {
10792             cfg.height=this.height
10793         }
10794         if (this.nowrap) {
10795             cfg.nowrap=this.nowrap
10796         }
10797         if (this.rowspan) {
10798             cfg.rowspan=this.rowspan
10799         }
10800         if (this.scope) {
10801             cfg.scope=this.scope
10802         }
10803         if (this.valign) {
10804             cfg.valign=this.valign
10805         }
10806         if (this.width) {
10807             cfg.width=this.width
10808         }
10809         
10810         
10811         return cfg;
10812     }
10813    
10814 });
10815
10816  
10817
10818  /*
10819  * - LGPL
10820  *
10821  * table row
10822  * 
10823  */
10824
10825 /**
10826  * @class Roo.bootstrap.TableRow
10827  * @extends Roo.bootstrap.Component
10828  * @children Roo.bootstrap.TableCell
10829  * @parent Roo.bootstrap.TableBody
10830  * Bootstrap TableRow class
10831  * @cfg {String} cls row class
10832  * @cfg {String} align Aligns the content in a table row
10833  * @cfg {String} bgcolor Specifies a background color for a table row
10834  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10835  * @cfg {String} valign Vertical aligns the content in a table row
10836  * 
10837  * @constructor
10838  * Create a new TableRow
10839  * @param {Object} config The config object
10840  */
10841
10842 Roo.bootstrap.TableRow = function(config){
10843     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10844 };
10845
10846 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10847     
10848     cls: false,
10849     align: false,
10850     bgcolor: false,
10851     charoff: false,
10852     valign: false,
10853     
10854     getAutoCreate : function(){
10855         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10856         
10857         cfg = {
10858             tag: 'tr'
10859         };
10860             
10861         if(this.cls){
10862             cfg.cls = this.cls;
10863         }
10864         if(this.align){
10865             cfg.align = this.align;
10866         }
10867         if(this.bgcolor){
10868             cfg.bgcolor = this.bgcolor;
10869         }
10870         if(this.charoff){
10871             cfg.charoff = this.charoff;
10872         }
10873         if(this.valign){
10874             cfg.valign = this.valign;
10875         }
10876         
10877         return cfg;
10878     }
10879    
10880 });
10881
10882  
10883
10884  /*
10885  * - LGPL
10886  *
10887  * table body
10888  * 
10889  */
10890
10891 /**
10892  * @class Roo.bootstrap.TableBody
10893  * @extends Roo.bootstrap.Component
10894  * @children Roo.bootstrap.TableRow
10895  * @parent Roo.bootstrap.Table
10896  * Bootstrap TableBody class
10897  * @cfg {String} cls element class
10898  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10899  * @cfg {String} align Aligns the content inside the element
10900  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10901  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10902  * 
10903  * @constructor
10904  * Create a new TableBody
10905  * @param {Object} config The config object
10906  */
10907
10908 Roo.bootstrap.TableBody = function(config){
10909     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10910 };
10911
10912 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10913     
10914     cls: false,
10915     tag: false,
10916     align: false,
10917     charoff: false,
10918     valign: false,
10919     
10920     getAutoCreate : function(){
10921         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10922         
10923         cfg = {
10924             tag: 'tbody'
10925         };
10926             
10927         if (this.cls) {
10928             cfg.cls=this.cls
10929         }
10930         if(this.tag){
10931             cfg.tag = this.tag;
10932         }
10933         
10934         if(this.align){
10935             cfg.align = this.align;
10936         }
10937         if(this.charoff){
10938             cfg.charoff = this.charoff;
10939         }
10940         if(this.valign){
10941             cfg.valign = this.valign;
10942         }
10943         
10944         return cfg;
10945     }
10946     
10947     
10948 //    initEvents : function()
10949 //    {
10950 //        
10951 //        if(!this.store){
10952 //            return;
10953 //        }
10954 //        
10955 //        this.store = Roo.factory(this.store, Roo.data);
10956 //        this.store.on('load', this.onLoad, this);
10957 //        
10958 //        this.store.load();
10959 //        
10960 //    },
10961 //    
10962 //    onLoad: function () 
10963 //    {   
10964 //        this.fireEvent('load', this);
10965 //    }
10966 //    
10967 //   
10968 });
10969
10970  
10971
10972  /*
10973  * Based on:
10974  * Ext JS Library 1.1.1
10975  * Copyright(c) 2006-2007, Ext JS, LLC.
10976  *
10977  * Originally Released Under LGPL - original licence link has changed is not relivant.
10978  *
10979  * Fork - LGPL
10980  * <script type="text/javascript">
10981  */
10982
10983 // as we use this in bootstrap.
10984 Roo.namespace('Roo.form');
10985  /**
10986  * @class Roo.form.Action
10987  * Internal Class used to handle form actions
10988  * @constructor
10989  * @param {Roo.form.BasicForm} el The form element or its id
10990  * @param {Object} config Configuration options
10991  */
10992
10993  
10994  
10995 // define the action interface
10996 Roo.form.Action = function(form, options){
10997     this.form = form;
10998     this.options = options || {};
10999 };
11000 /**
11001  * Client Validation Failed
11002  * @const 
11003  */
11004 Roo.form.Action.CLIENT_INVALID = 'client';
11005 /**
11006  * Server Validation Failed
11007  * @const 
11008  */
11009 Roo.form.Action.SERVER_INVALID = 'server';
11010  /**
11011  * Connect to Server Failed
11012  * @const 
11013  */
11014 Roo.form.Action.CONNECT_FAILURE = 'connect';
11015 /**
11016  * Reading Data from Server Failed
11017  * @const 
11018  */
11019 Roo.form.Action.LOAD_FAILURE = 'load';
11020
11021 Roo.form.Action.prototype = {
11022     type : 'default',
11023     failureType : undefined,
11024     response : undefined,
11025     result : undefined,
11026
11027     // interface method
11028     run : function(options){
11029
11030     },
11031
11032     // interface method
11033     success : function(response){
11034
11035     },
11036
11037     // interface method
11038     handleResponse : function(response){
11039
11040     },
11041
11042     // default connection failure
11043     failure : function(response){
11044         
11045         this.response = response;
11046         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11047         this.form.afterAction(this, false);
11048     },
11049
11050     processResponse : function(response){
11051         this.response = response;
11052         if(!response.responseText){
11053             return true;
11054         }
11055         this.result = this.handleResponse(response);
11056         return this.result;
11057     },
11058
11059     // utility functions used internally
11060     getUrl : function(appendParams){
11061         var url = this.options.url || this.form.url || this.form.el.dom.action;
11062         if(appendParams){
11063             var p = this.getParams();
11064             if(p){
11065                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11066             }
11067         }
11068         return url;
11069     },
11070
11071     getMethod : function(){
11072         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11073     },
11074
11075     getParams : function(){
11076         var bp = this.form.baseParams;
11077         var p = this.options.params;
11078         if(p){
11079             if(typeof p == "object"){
11080                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11081             }else if(typeof p == 'string' && bp){
11082                 p += '&' + Roo.urlEncode(bp);
11083             }
11084         }else if(bp){
11085             p = Roo.urlEncode(bp);
11086         }
11087         return p;
11088     },
11089
11090     createCallback : function(){
11091         return {
11092             success: this.success,
11093             failure: this.failure,
11094             scope: this,
11095             timeout: (this.form.timeout*1000),
11096             upload: this.form.fileUpload ? this.success : undefined
11097         };
11098     }
11099 };
11100
11101 Roo.form.Action.Submit = function(form, options){
11102     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11103 };
11104
11105 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11106     type : 'submit',
11107
11108     haveProgress : false,
11109     uploadComplete : false,
11110     
11111     // uploadProgress indicator.
11112     uploadProgress : function()
11113     {
11114         if (!this.form.progressUrl) {
11115             return;
11116         }
11117         
11118         if (!this.haveProgress) {
11119             Roo.MessageBox.progress("Uploading", "Uploading");
11120         }
11121         if (this.uploadComplete) {
11122            Roo.MessageBox.hide();
11123            return;
11124         }
11125         
11126         this.haveProgress = true;
11127    
11128         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11129         
11130         var c = new Roo.data.Connection();
11131         c.request({
11132             url : this.form.progressUrl,
11133             params: {
11134                 id : uid
11135             },
11136             method: 'GET',
11137             success : function(req){
11138                //console.log(data);
11139                 var rdata = false;
11140                 var edata;
11141                 try  {
11142                    rdata = Roo.decode(req.responseText)
11143                 } catch (e) {
11144                     Roo.log("Invalid data from server..");
11145                     Roo.log(edata);
11146                     return;
11147                 }
11148                 if (!rdata || !rdata.success) {
11149                     Roo.log(rdata);
11150                     Roo.MessageBox.alert(Roo.encode(rdata));
11151                     return;
11152                 }
11153                 var data = rdata.data;
11154                 
11155                 if (this.uploadComplete) {
11156                    Roo.MessageBox.hide();
11157                    return;
11158                 }
11159                    
11160                 if (data){
11161                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11162                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11163                     );
11164                 }
11165                 this.uploadProgress.defer(2000,this);
11166             },
11167        
11168             failure: function(data) {
11169                 Roo.log('progress url failed ');
11170                 Roo.log(data);
11171             },
11172             scope : this
11173         });
11174            
11175     },
11176     
11177     
11178     run : function()
11179     {
11180         // run get Values on the form, so it syncs any secondary forms.
11181         this.form.getValues();
11182         
11183         var o = this.options;
11184         var method = this.getMethod();
11185         var isPost = method == 'POST';
11186         if(o.clientValidation === false || this.form.isValid()){
11187             
11188             if (this.form.progressUrl) {
11189                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11190                     (new Date() * 1) + '' + Math.random());
11191                     
11192             } 
11193             
11194             
11195             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11196                 form:this.form.el.dom,
11197                 url:this.getUrl(!isPost),
11198                 method: method,
11199                 params:isPost ? this.getParams() : null,
11200                 isUpload: this.form.fileUpload,
11201                 formData : this.form.formData
11202             }));
11203             
11204             this.uploadProgress();
11205
11206         }else if (o.clientValidation !== false){ // client validation failed
11207             this.failureType = Roo.form.Action.CLIENT_INVALID;
11208             this.form.afterAction(this, false);
11209         }
11210     },
11211
11212     success : function(response)
11213     {
11214         this.uploadComplete= true;
11215         if (this.haveProgress) {
11216             Roo.MessageBox.hide();
11217         }
11218         
11219         
11220         var result = this.processResponse(response);
11221         if(result === true || result.success){
11222             this.form.afterAction(this, true);
11223             return;
11224         }
11225         if(result.errors){
11226             this.form.markInvalid(result.errors);
11227             this.failureType = Roo.form.Action.SERVER_INVALID;
11228         }
11229         this.form.afterAction(this, false);
11230     },
11231     failure : function(response)
11232     {
11233         this.uploadComplete= true;
11234         if (this.haveProgress) {
11235             Roo.MessageBox.hide();
11236         }
11237         
11238         this.response = response;
11239         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11240         this.form.afterAction(this, false);
11241     },
11242     
11243     handleResponse : function(response){
11244         if(this.form.errorReader){
11245             var rs = this.form.errorReader.read(response);
11246             var errors = [];
11247             if(rs.records){
11248                 for(var i = 0, len = rs.records.length; i < len; i++) {
11249                     var r = rs.records[i];
11250                     errors[i] = r.data;
11251                 }
11252             }
11253             if(errors.length < 1){
11254                 errors = null;
11255             }
11256             return {
11257                 success : rs.success,
11258                 errors : errors
11259             };
11260         }
11261         var ret = false;
11262         try {
11263             ret = Roo.decode(response.responseText);
11264         } catch (e) {
11265             ret = {
11266                 success: false,
11267                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11268                 errors : []
11269             };
11270         }
11271         return ret;
11272         
11273     }
11274 });
11275
11276
11277 Roo.form.Action.Load = function(form, options){
11278     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11279     this.reader = this.form.reader;
11280 };
11281
11282 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11283     type : 'load',
11284
11285     run : function(){
11286         
11287         Roo.Ajax.request(Roo.apply(
11288                 this.createCallback(), {
11289                     method:this.getMethod(),
11290                     url:this.getUrl(false),
11291                     params:this.getParams()
11292         }));
11293     },
11294
11295     success : function(response){
11296         
11297         var result = this.processResponse(response);
11298         if(result === true || !result.success || !result.data){
11299             this.failureType = Roo.form.Action.LOAD_FAILURE;
11300             this.form.afterAction(this, false);
11301             return;
11302         }
11303         this.form.clearInvalid();
11304         this.form.setValues(result.data);
11305         this.form.afterAction(this, true);
11306     },
11307
11308     handleResponse : function(response){
11309         if(this.form.reader){
11310             var rs = this.form.reader.read(response);
11311             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11312             return {
11313                 success : rs.success,
11314                 data : data
11315             };
11316         }
11317         return Roo.decode(response.responseText);
11318     }
11319 });
11320
11321 Roo.form.Action.ACTION_TYPES = {
11322     'load' : Roo.form.Action.Load,
11323     'submit' : Roo.form.Action.Submit
11324 };/*
11325  * - LGPL
11326  *
11327  * form
11328  *
11329  */
11330
11331 /**
11332  * @class Roo.bootstrap.form.Form
11333  * @extends Roo.bootstrap.Component
11334  * @children Roo.bootstrap.Component
11335  * Bootstrap Form class
11336  * @cfg {String} method  GET | POST (default POST)
11337  * @cfg {String} labelAlign top | left (default top)
11338  * @cfg {String} align left  | right - for navbars
11339  * @cfg {Boolean} loadMask load mask when submit (default true)
11340
11341  *
11342  * @constructor
11343  * Create a new Form
11344  * @param {Object} config The config object
11345  */
11346
11347
11348 Roo.bootstrap.form.Form = function(config){
11349     
11350     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11351     
11352     Roo.bootstrap.form.Form.popover.apply();
11353     
11354     this.addEvents({
11355         /**
11356          * @event clientvalidation
11357          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11358          * @param {Form} this
11359          * @param {Boolean} valid true if the form has passed client-side validation
11360          */
11361         clientvalidation: true,
11362         /**
11363          * @event beforeaction
11364          * Fires before any action is performed. Return false to cancel the action.
11365          * @param {Form} this
11366          * @param {Action} action The action to be performed
11367          */
11368         beforeaction: true,
11369         /**
11370          * @event actionfailed
11371          * Fires when an action fails.
11372          * @param {Form} this
11373          * @param {Action} action The action that failed
11374          */
11375         actionfailed : true,
11376         /**
11377          * @event actioncomplete
11378          * Fires when an action is completed.
11379          * @param {Form} this
11380          * @param {Action} action The action that completed
11381          */
11382         actioncomplete : true
11383     });
11384 };
11385
11386 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11387
11388      /**
11389      * @cfg {String} method
11390      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11391      */
11392     method : 'POST',
11393     /**
11394      * @cfg {String} url
11395      * The URL to use for form actions if one isn't supplied in the action options.
11396      */
11397     /**
11398      * @cfg {Boolean} fileUpload
11399      * Set to true if this form is a file upload.
11400      */
11401
11402     /**
11403      * @cfg {Object} baseParams
11404      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11405      */
11406
11407     /**
11408      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11409      */
11410     timeout: 30,
11411     /**
11412      * @cfg {Sting} align (left|right) for navbar forms
11413      */
11414     align : 'left',
11415
11416     // private
11417     activeAction : null,
11418
11419     /**
11420      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11421      * element by passing it or its id or mask the form itself by passing in true.
11422      * @type Mixed
11423      */
11424     waitMsgTarget : false,
11425
11426     loadMask : true,
11427     
11428     /**
11429      * @cfg {Boolean} errorMask (true|false) default false
11430      */
11431     errorMask : false,
11432     
11433     /**
11434      * @cfg {Number} maskOffset Default 100
11435      */
11436     maskOffset : 100,
11437     
11438     /**
11439      * @cfg {Boolean} maskBody
11440      */
11441     maskBody : false,
11442
11443     getAutoCreate : function(){
11444
11445         var cfg = {
11446             tag: 'form',
11447             method : this.method || 'POST',
11448             id : this.id || Roo.id(),
11449             cls : ''
11450         };
11451         if (this.parent().xtype.match(/^Nav/)) {
11452             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11453
11454         }
11455
11456         if (this.labelAlign == 'left' ) {
11457             cfg.cls += ' form-horizontal';
11458         }
11459
11460
11461         return cfg;
11462     },
11463     initEvents : function()
11464     {
11465         this.el.on('submit', this.onSubmit, this);
11466         // this was added as random key presses on the form where triggering form submit.
11467         this.el.on('keypress', function(e) {
11468             if (e.getCharCode() != 13) {
11469                 return true;
11470             }
11471             // we might need to allow it for textareas.. and some other items.
11472             // check e.getTarget().
11473
11474             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11475                 return true;
11476             }
11477
11478             Roo.log("keypress blocked");
11479
11480             e.preventDefault();
11481             return false;
11482         });
11483         
11484     },
11485     // private
11486     onSubmit : function(e){
11487         e.stopEvent();
11488     },
11489
11490      /**
11491      * Returns true if client-side validation on the form is successful.
11492      * @return Boolean
11493      */
11494     isValid : function(){
11495         var items = this.getItems();
11496         var valid = true;
11497         var target = false;
11498         
11499         items.each(function(f){
11500             
11501             if(f.validate()){
11502                 return;
11503             }
11504             
11505             Roo.log('invalid field: ' + f.name);
11506             
11507             valid = false;
11508
11509             if(!target && f.el.isVisible(true)){
11510                 target = f;
11511             }
11512            
11513         });
11514         
11515         if(this.errorMask && !valid){
11516             Roo.bootstrap.form.Form.popover.mask(this, target);
11517         }
11518         
11519         return valid;
11520     },
11521     
11522     /**
11523      * Returns true if any fields in this form have changed since their original load.
11524      * @return Boolean
11525      */
11526     isDirty : function(){
11527         var dirty = false;
11528         var items = this.getItems();
11529         items.each(function(f){
11530            if(f.isDirty()){
11531                dirty = true;
11532                return false;
11533            }
11534            return true;
11535         });
11536         return dirty;
11537     },
11538      /**
11539      * Performs a predefined action (submit or load) or custom actions you define on this form.
11540      * @param {String} actionName The name of the action type
11541      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11542      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11543      * accept other config options):
11544      * <pre>
11545 Property          Type             Description
11546 ----------------  ---------------  ----------------------------------------------------------------------------------
11547 url               String           The url for the action (defaults to the form's url)
11548 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11549 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11550 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11551                                    validate the form on the client (defaults to false)
11552      * </pre>
11553      * @return {BasicForm} this
11554      */
11555     doAction : function(action, options){
11556         if(typeof action == 'string'){
11557             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11558         }
11559         if(this.fireEvent('beforeaction', this, action) !== false){
11560             this.beforeAction(action);
11561             action.run.defer(100, action);
11562         }
11563         return this;
11564     },
11565
11566     // private
11567     beforeAction : function(action){
11568         var o = action.options;
11569         
11570         if(this.loadMask){
11571             
11572             if(this.maskBody){
11573                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11574             } else {
11575                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11576             }
11577         }
11578         // not really supported yet.. ??
11579
11580         //if(this.waitMsgTarget === true){
11581         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11582         //}else if(this.waitMsgTarget){
11583         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11584         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11585         //}else {
11586         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11587        // }
11588
11589     },
11590
11591     // private
11592     afterAction : function(action, success){
11593         this.activeAction = null;
11594         var o = action.options;
11595
11596         if(this.loadMask){
11597             
11598             if(this.maskBody){
11599                 Roo.get(document.body).unmask();
11600             } else {
11601                 this.el.unmask();
11602             }
11603         }
11604         
11605         //if(this.waitMsgTarget === true){
11606 //            this.el.unmask();
11607         //}else if(this.waitMsgTarget){
11608         //    this.waitMsgTarget.unmask();
11609         //}else{
11610         //    Roo.MessageBox.updateProgress(1);
11611         //    Roo.MessageBox.hide();
11612        // }
11613         //
11614         if(success){
11615             if(o.reset){
11616                 this.reset();
11617             }
11618             Roo.callback(o.success, o.scope, [this, action]);
11619             this.fireEvent('actioncomplete', this, action);
11620
11621         }else{
11622
11623             // failure condition..
11624             // we have a scenario where updates need confirming.
11625             // eg. if a locking scenario exists..
11626             // we look for { errors : { needs_confirm : true }} in the response.
11627             if (
11628                 (typeof(action.result) != 'undefined')  &&
11629                 (typeof(action.result.errors) != 'undefined')  &&
11630                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11631            ){
11632                 var _t = this;
11633                 Roo.log("not supported yet");
11634                  /*
11635
11636                 Roo.MessageBox.confirm(
11637                     "Change requires confirmation",
11638                     action.result.errorMsg,
11639                     function(r) {
11640                         if (r != 'yes') {
11641                             return;
11642                         }
11643                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11644                     }
11645
11646                 );
11647                 */
11648
11649
11650                 return;
11651             }
11652
11653             Roo.callback(o.failure, o.scope, [this, action]);
11654             // show an error message if no failed handler is set..
11655             if (!this.hasListener('actionfailed')) {
11656                 Roo.log("need to add dialog support");
11657                 /*
11658                 Roo.MessageBox.alert("Error",
11659                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11660                         action.result.errorMsg :
11661                         "Saving Failed, please check your entries or try again"
11662                 );
11663                 */
11664             }
11665
11666             this.fireEvent('actionfailed', this, action);
11667         }
11668
11669     },
11670     /**
11671      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11672      * @param {String} id The value to search for
11673      * @return Field
11674      */
11675     findField : function(id){
11676         var items = this.getItems();
11677         var field = items.get(id);
11678         if(!field){
11679              items.each(function(f){
11680                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11681                     field = f;
11682                     return false;
11683                 }
11684                 return true;
11685             });
11686         }
11687         return field || null;
11688     },
11689      /**
11690      * Mark fields in this form invalid in bulk.
11691      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11692      * @return {BasicForm} this
11693      */
11694     markInvalid : function(errors){
11695         if(errors instanceof Array){
11696             for(var i = 0, len = errors.length; i < len; i++){
11697                 var fieldError = errors[i];
11698                 var f = this.findField(fieldError.id);
11699                 if(f){
11700                     f.markInvalid(fieldError.msg);
11701                 }
11702             }
11703         }else{
11704             var field, id;
11705             for(id in errors){
11706                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11707                     field.markInvalid(errors[id]);
11708                 }
11709             }
11710         }
11711         //Roo.each(this.childForms || [], function (f) {
11712         //    f.markInvalid(errors);
11713         //});
11714
11715         return this;
11716     },
11717
11718     /**
11719      * Set values for fields in this form in bulk.
11720      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11721      * @return {BasicForm} this
11722      */
11723     setValues : function(values){
11724         if(values instanceof Array){ // array of objects
11725             for(var i = 0, len = values.length; i < len; i++){
11726                 var v = values[i];
11727                 var f = this.findField(v.id);
11728                 if(f){
11729                     f.setValue(v.value);
11730                     if(this.trackResetOnLoad){
11731                         f.originalValue = f.getValue();
11732                     }
11733                 }
11734             }
11735         }else{ // object hash
11736             var field, id;
11737             for(id in values){
11738                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11739
11740                     if (field.setFromData &&
11741                         field.valueField &&
11742                         field.displayField &&
11743                         // combos' with local stores can
11744                         // be queried via setValue()
11745                         // to set their value..
11746                         (field.store && !field.store.isLocal)
11747                         ) {
11748                         // it's a combo
11749                         var sd = { };
11750                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11751                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11752                         field.setFromData(sd);
11753
11754                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11755                         
11756                         field.setFromData(values);
11757                         
11758                     } else {
11759                         field.setValue(values[id]);
11760                     }
11761
11762
11763                     if(this.trackResetOnLoad){
11764                         field.originalValue = field.getValue();
11765                     }
11766                 }
11767             }
11768         }
11769
11770         //Roo.each(this.childForms || [], function (f) {
11771         //    f.setValues(values);
11772         //});
11773
11774         return this;
11775     },
11776
11777     /**
11778      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11779      * they are returned as an array.
11780      * @param {Boolean} asString
11781      * @return {Object}
11782      */
11783     getValues : function(asString){
11784         //if (this.childForms) {
11785             // copy values from the child forms
11786         //    Roo.each(this.childForms, function (f) {
11787         //        this.setValues(f.getValues());
11788         //    }, this);
11789         //}
11790
11791
11792
11793         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11794         if(asString === true){
11795             return fs;
11796         }
11797         return Roo.urlDecode(fs);
11798     },
11799
11800     /**
11801      * Returns the fields in this form as an object with key/value pairs.
11802      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11803      * @return {Object}
11804      */
11805     getFieldValues : function(with_hidden)
11806     {
11807         var items = this.getItems();
11808         var ret = {};
11809         items.each(function(f){
11810             
11811             if (!f.getName()) {
11812                 return;
11813             }
11814             
11815             var v = f.getValue();
11816             
11817             if (f.inputType =='radio') {
11818                 if (typeof(ret[f.getName()]) == 'undefined') {
11819                     ret[f.getName()] = ''; // empty..
11820                 }
11821
11822                 if (!f.el.dom.checked) {
11823                     return;
11824
11825                 }
11826                 v = f.el.dom.value;
11827
11828             }
11829             
11830             if(f.xtype == 'MoneyField'){
11831                 ret[f.currencyName] = f.getCurrency();
11832             }
11833
11834             // not sure if this supported any more..
11835             if ((typeof(v) == 'object') && f.getRawValue) {
11836                 v = f.getRawValue() ; // dates..
11837             }
11838             // combo boxes where name != hiddenName...
11839             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11840                 ret[f.name] = f.getRawValue();
11841             }
11842             ret[f.getName()] = v;
11843         });
11844
11845         return ret;
11846     },
11847
11848     /**
11849      * Clears all invalid messages in this form.
11850      * @return {BasicForm} this
11851      */
11852     clearInvalid : function(){
11853         var items = this.getItems();
11854
11855         items.each(function(f){
11856            f.clearInvalid();
11857         });
11858
11859         return this;
11860     },
11861
11862     /**
11863      * Resets this form.
11864      * @return {BasicForm} this
11865      */
11866     reset : function(){
11867         var items = this.getItems();
11868         items.each(function(f){
11869             f.reset();
11870         });
11871
11872         Roo.each(this.childForms || [], function (f) {
11873             f.reset();
11874         });
11875
11876
11877         return this;
11878     },
11879     
11880     getItems : function()
11881     {
11882         var r=new Roo.util.MixedCollection(false, function(o){
11883             return o.id || (o.id = Roo.id());
11884         });
11885         var iter = function(el) {
11886             if (el.inputEl) {
11887                 r.add(el);
11888             }
11889             if (!el.items) {
11890                 return;
11891             }
11892             Roo.each(el.items,function(e) {
11893                 iter(e);
11894             });
11895         };
11896
11897         iter(this);
11898         return r;
11899     },
11900     
11901     hideFields : function(items)
11902     {
11903         Roo.each(items, function(i){
11904             
11905             var f = this.findField(i);
11906             
11907             if(!f){
11908                 return;
11909             }
11910             
11911             f.hide();
11912             
11913         }, this);
11914     },
11915     
11916     showFields : function(items)
11917     {
11918         Roo.each(items, function(i){
11919             
11920             var f = this.findField(i);
11921             
11922             if(!f){
11923                 return;
11924             }
11925             
11926             f.show();
11927             
11928         }, this);
11929     }
11930
11931 });
11932
11933 Roo.apply(Roo.bootstrap.form.Form, {
11934     
11935     popover : {
11936         
11937         padding : 5,
11938         
11939         isApplied : false,
11940         
11941         isMasked : false,
11942         
11943         form : false,
11944         
11945         target : false,
11946         
11947         toolTip : false,
11948         
11949         intervalID : false,
11950         
11951         maskEl : false,
11952         
11953         apply : function()
11954         {
11955             if(this.isApplied){
11956                 return;
11957             }
11958             
11959             this.maskEl = {
11960                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11961                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11962                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11963                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11964             };
11965             
11966             this.maskEl.top.enableDisplayMode("block");
11967             this.maskEl.left.enableDisplayMode("block");
11968             this.maskEl.bottom.enableDisplayMode("block");
11969             this.maskEl.right.enableDisplayMode("block");
11970             
11971             this.toolTip = new Roo.bootstrap.Tooltip({
11972                 cls : 'roo-form-error-popover',
11973                 alignment : {
11974                     'left' : ['r-l', [-2,0], 'right'],
11975                     'right' : ['l-r', [2,0], 'left'],
11976                     'bottom' : ['tl-bl', [0,2], 'top'],
11977                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11978                 }
11979             });
11980             
11981             this.toolTip.render(Roo.get(document.body));
11982
11983             this.toolTip.el.enableDisplayMode("block");
11984             
11985             Roo.get(document.body).on('click', function(){
11986                 this.unmask();
11987             }, this);
11988             
11989             Roo.get(document.body).on('touchstart', function(){
11990                 this.unmask();
11991             }, this);
11992             
11993             this.isApplied = true
11994         },
11995         
11996         mask : function(form, target)
11997         {
11998             this.form = form;
11999             
12000             this.target = target;
12001             
12002             if(!this.form.errorMask || !target.el){
12003                 return;
12004             }
12005             
12006             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12007             
12008             Roo.log(scrollable);
12009             
12010             var ot = this.target.el.calcOffsetsTo(scrollable);
12011             
12012             var scrollTo = ot[1] - this.form.maskOffset;
12013             
12014             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12015             
12016             scrollable.scrollTo('top', scrollTo);
12017             
12018             var box = this.target.el.getBox();
12019             Roo.log(box);
12020             var zIndex = Roo.bootstrap.Modal.zIndex++;
12021
12022             
12023             this.maskEl.top.setStyle('position', 'absolute');
12024             this.maskEl.top.setStyle('z-index', zIndex);
12025             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12026             this.maskEl.top.setLeft(0);
12027             this.maskEl.top.setTop(0);
12028             this.maskEl.top.show();
12029             
12030             this.maskEl.left.setStyle('position', 'absolute');
12031             this.maskEl.left.setStyle('z-index', zIndex);
12032             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12033             this.maskEl.left.setLeft(0);
12034             this.maskEl.left.setTop(box.y - this.padding);
12035             this.maskEl.left.show();
12036
12037             this.maskEl.bottom.setStyle('position', 'absolute');
12038             this.maskEl.bottom.setStyle('z-index', zIndex);
12039             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12040             this.maskEl.bottom.setLeft(0);
12041             this.maskEl.bottom.setTop(box.bottom + this.padding);
12042             this.maskEl.bottom.show();
12043
12044             this.maskEl.right.setStyle('position', 'absolute');
12045             this.maskEl.right.setStyle('z-index', zIndex);
12046             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12047             this.maskEl.right.setLeft(box.right + this.padding);
12048             this.maskEl.right.setTop(box.y - this.padding);
12049             this.maskEl.right.show();
12050
12051             this.toolTip.bindEl = this.target.el;
12052
12053             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12054
12055             var tip = this.target.blankText;
12056
12057             if(this.target.getValue() !== '' ) {
12058                 
12059                 if (this.target.invalidText.length) {
12060                     tip = this.target.invalidText;
12061                 } else if (this.target.regexText.length){
12062                     tip = this.target.regexText;
12063                 }
12064             }
12065
12066             this.toolTip.show(tip);
12067
12068             this.intervalID = window.setInterval(function() {
12069                 Roo.bootstrap.form.Form.popover.unmask();
12070             }, 10000);
12071
12072             window.onwheel = function(){ return false;};
12073             
12074             (function(){ this.isMasked = true; }).defer(500, this);
12075             
12076         },
12077         
12078         unmask : function()
12079         {
12080             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12081                 return;
12082             }
12083             
12084             this.maskEl.top.setStyle('position', 'absolute');
12085             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12086             this.maskEl.top.hide();
12087
12088             this.maskEl.left.setStyle('position', 'absolute');
12089             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12090             this.maskEl.left.hide();
12091
12092             this.maskEl.bottom.setStyle('position', 'absolute');
12093             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12094             this.maskEl.bottom.hide();
12095
12096             this.maskEl.right.setStyle('position', 'absolute');
12097             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12098             this.maskEl.right.hide();
12099             
12100             this.toolTip.hide();
12101             
12102             this.toolTip.el.hide();
12103             
12104             window.onwheel = function(){ return true;};
12105             
12106             if(this.intervalID){
12107                 window.clearInterval(this.intervalID);
12108                 this.intervalID = false;
12109             }
12110             
12111             this.isMasked = false;
12112             
12113         }
12114         
12115     }
12116     
12117 });
12118
12119 /*
12120  * Based on:
12121  * Ext JS Library 1.1.1
12122  * Copyright(c) 2006-2007, Ext JS, LLC.
12123  *
12124  * Originally Released Under LGPL - original licence link has changed is not relivant.
12125  *
12126  * Fork - LGPL
12127  * <script type="text/javascript">
12128  */
12129 /**
12130  * @class Roo.form.VTypes
12131  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12132  * @singleton
12133  */
12134 Roo.form.VTypes = function(){
12135     // closure these in so they are only created once.
12136     var alpha = /^[a-zA-Z_]+$/;
12137     var alphanum = /^[a-zA-Z0-9_]+$/;
12138     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12139     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12140
12141     // All these messages and functions are configurable
12142     return {
12143         /**
12144          * The function used to validate email addresses
12145          * @param {String} value The email address
12146          */
12147         'email' : function(v){
12148             return email.test(v);
12149         },
12150         /**
12151          * The error text to display when the email validation function returns false
12152          * @type String
12153          */
12154         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12155         /**
12156          * The keystroke filter mask to be applied on email input
12157          * @type RegExp
12158          */
12159         'emailMask' : /[a-z0-9_\.\-@]/i,
12160
12161         /**
12162          * The function used to validate URLs
12163          * @param {String} value The URL
12164          */
12165         'url' : function(v){
12166             return url.test(v);
12167         },
12168         /**
12169          * The error text to display when the url validation function returns false
12170          * @type String
12171          */
12172         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12173         
12174         /**
12175          * The function used to validate alpha values
12176          * @param {String} value The value
12177          */
12178         'alpha' : function(v){
12179             return alpha.test(v);
12180         },
12181         /**
12182          * The error text to display when the alpha validation function returns false
12183          * @type String
12184          */
12185         'alphaText' : 'This field should only contain letters and _',
12186         /**
12187          * The keystroke filter mask to be applied on alpha input
12188          * @type RegExp
12189          */
12190         'alphaMask' : /[a-z_]/i,
12191
12192         /**
12193          * The function used to validate alphanumeric values
12194          * @param {String} value The value
12195          */
12196         'alphanum' : function(v){
12197             return alphanum.test(v);
12198         },
12199         /**
12200          * The error text to display when the alphanumeric validation function returns false
12201          * @type String
12202          */
12203         'alphanumText' : 'This field should only contain letters, numbers and _',
12204         /**
12205          * The keystroke filter mask to be applied on alphanumeric input
12206          * @type RegExp
12207          */
12208         'alphanumMask' : /[a-z0-9_]/i
12209     };
12210 }();/*
12211  * - LGPL
12212  *
12213  * Input
12214  * 
12215  */
12216
12217 /**
12218  * @class Roo.bootstrap.form.Input
12219  * @extends Roo.bootstrap.Component
12220  * Bootstrap Input class
12221  * @cfg {Boolean} disabled is it disabled
12222  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12223  * @cfg {String} name name of the input
12224  * @cfg {string} fieldLabel - the label associated
12225  * @cfg {string} placeholder - placeholder to put in text.
12226  * @cfg {string} before - input group add on before
12227  * @cfg {string} after - input group add on after
12228  * @cfg {string} size - (lg|sm) or leave empty..
12229  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12230  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12231  * @cfg {Number} md colspan out of 12 for computer-sized screens
12232  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12233  * @cfg {string} value default value of the input
12234  * @cfg {Number} labelWidth set the width of label 
12235  * @cfg {Number} labellg set the width of label (1-12)
12236  * @cfg {Number} labelmd set the width of label (1-12)
12237  * @cfg {Number} labelsm set the width of label (1-12)
12238  * @cfg {Number} labelxs set the width of label (1-12)
12239  * @cfg {String} labelAlign (top|left)
12240  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12241  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12242  * @cfg {String} indicatorpos (left|right) default left
12243  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12244  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12245  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12246  * @cfg {Roo.bootstrap.Button} before Button to show before
12247  * @cfg {Roo.bootstrap.Button} afterButton to show before
12248  * @cfg {String} align (left|center|right) Default left
12249  * @cfg {Boolean} forceFeedback (true|false) Default false
12250  * 
12251  * @constructor
12252  * Create a new Input
12253  * @param {Object} config The config object
12254  */
12255
12256 Roo.bootstrap.form.Input = function(config){
12257     
12258     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12259     
12260     this.addEvents({
12261         /**
12262          * @event focus
12263          * Fires when this field receives input focus.
12264          * @param {Roo.form.Field} this
12265          */
12266         focus : true,
12267         /**
12268          * @event blur
12269          * Fires when this field loses input focus.
12270          * @param {Roo.form.Field} this
12271          */
12272         blur : true,
12273         /**
12274          * @event specialkey
12275          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12276          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12277          * @param {Roo.form.Field} this
12278          * @param {Roo.EventObject} e The event object
12279          */
12280         specialkey : true,
12281         /**
12282          * @event change
12283          * Fires just before the field blurs if the field value has changed.
12284          * @param {Roo.form.Field} this
12285          * @param {Mixed} newValue The new value
12286          * @param {Mixed} oldValue The original value
12287          */
12288         change : true,
12289         /**
12290          * @event invalid
12291          * Fires after the field has been marked as invalid.
12292          * @param {Roo.form.Field} this
12293          * @param {String} msg The validation message
12294          */
12295         invalid : true,
12296         /**
12297          * @event valid
12298          * Fires after the field has been validated with no errors.
12299          * @param {Roo.form.Field} this
12300          */
12301         valid : true,
12302          /**
12303          * @event keyup
12304          * Fires after the key up
12305          * @param {Roo.form.Field} this
12306          * @param {Roo.EventObject}  e The event Object
12307          */
12308         keyup : true,
12309         /**
12310          * @event paste
12311          * Fires after the user pastes into input
12312          * @param {Roo.form.Field} this
12313          * @param {Roo.EventObject}  e The event Object
12314          */
12315         paste : true
12316     });
12317 };
12318
12319 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12320      /**
12321      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12322       automatic validation (defaults to "keyup").
12323      */
12324     validationEvent : "keyup",
12325      /**
12326      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12327      */
12328     validateOnBlur : true,
12329     /**
12330      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12331      */
12332     validationDelay : 250,
12333      /**
12334      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12335      */
12336     focusClass : "x-form-focus",  // not needed???
12337     
12338        
12339     /**
12340      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12341      */
12342     invalidClass : "has-warning",
12343     
12344     /**
12345      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12346      */
12347     validClass : "has-success",
12348     
12349     /**
12350      * @cfg {Boolean} hasFeedback (true|false) default true
12351      */
12352     hasFeedback : true,
12353     
12354     /**
12355      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12356      */
12357     invalidFeedbackClass : "glyphicon-warning-sign",
12358     
12359     /**
12360      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12361      */
12362     validFeedbackClass : "glyphicon-ok",
12363     
12364     /**
12365      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12366      */
12367     selectOnFocus : false,
12368     
12369      /**
12370      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12371      */
12372     maskRe : null,
12373        /**
12374      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12375      */
12376     vtype : null,
12377     
12378       /**
12379      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12380      */
12381     disableKeyFilter : false,
12382     
12383        /**
12384      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12385      */
12386     disabled : false,
12387      /**
12388      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12389      */
12390     allowBlank : true,
12391     /**
12392      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12393      */
12394     blankText : "Please complete this mandatory field",
12395     
12396      /**
12397      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12398      */
12399     minLength : 0,
12400     /**
12401      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12402      */
12403     maxLength : Number.MAX_VALUE,
12404     /**
12405      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12406      */
12407     minLengthText : "The minimum length for this field is {0}",
12408     /**
12409      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12410      */
12411     maxLengthText : "The maximum length for this field is {0}",
12412   
12413     
12414     /**
12415      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12416      * If available, this function will be called only after the basic validators all return true, and will be passed the
12417      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12418      */
12419     validator : null,
12420     /**
12421      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12422      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12423      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12424      */
12425     regex : null,
12426     /**
12427      * @cfg {String} regexText -- Depricated - use Invalid Text
12428      */
12429     regexText : "",
12430     
12431     /**
12432      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12433      */
12434     invalidText : "",
12435     
12436     
12437     
12438     autocomplete: false,
12439     
12440     
12441     fieldLabel : '',
12442     inputType : 'text',
12443     
12444     name : false,
12445     placeholder: false,
12446     before : false,
12447     after : false,
12448     size : false,
12449     hasFocus : false,
12450     preventMark: false,
12451     isFormField : true,
12452     value : '',
12453     labelWidth : 2,
12454     labelAlign : false,
12455     readOnly : false,
12456     align : false,
12457     formatedValue : false,
12458     forceFeedback : false,
12459     
12460     indicatorpos : 'left',
12461     
12462     labellg : 0,
12463     labelmd : 0,
12464     labelsm : 0,
12465     labelxs : 0,
12466     
12467     capture : '',
12468     accept : '',
12469     
12470     parentLabelAlign : function()
12471     {
12472         var parent = this;
12473         while (parent.parent()) {
12474             parent = parent.parent();
12475             if (typeof(parent.labelAlign) !='undefined') {
12476                 return parent.labelAlign;
12477             }
12478         }
12479         return 'left';
12480         
12481     },
12482     
12483     getAutoCreate : function()
12484     {
12485         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12486         
12487         var id = Roo.id();
12488         
12489         var cfg = {};
12490         
12491         if(this.inputType != 'hidden'){
12492             cfg.cls = 'form-group' //input-group
12493         }
12494         
12495         var input =  {
12496             tag: 'input',
12497             id : id,
12498             type : this.inputType,
12499             value : this.value,
12500             cls : 'form-control',
12501             placeholder : this.placeholder || '',
12502             autocomplete : this.autocomplete || 'new-password'
12503         };
12504         if (this.inputType == 'file') {
12505             input.style = 'overflow:hidden'; // why not in CSS?
12506         }
12507         
12508         if(this.capture.length){
12509             input.capture = this.capture;
12510         }
12511         
12512         if(this.accept.length){
12513             input.accept = this.accept + "/*";
12514         }
12515         
12516         if(this.align){
12517             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12518         }
12519         
12520         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12521             input.maxLength = this.maxLength;
12522         }
12523         
12524         if (this.disabled) {
12525             input.disabled=true;
12526         }
12527         
12528         if (this.readOnly) {
12529             input.readonly=true;
12530         }
12531         
12532         if (this.name) {
12533             input.name = this.name;
12534         }
12535         
12536         if (this.size) {
12537             input.cls += ' input-' + this.size;
12538         }
12539         
12540         var settings=this;
12541         ['xs','sm','md','lg'].map(function(size){
12542             if (settings[size]) {
12543                 cfg.cls += ' col-' + size + '-' + settings[size];
12544             }
12545         });
12546         
12547         var inputblock = input;
12548         
12549         var feedback = {
12550             tag: 'span',
12551             cls: 'glyphicon form-control-feedback'
12552         };
12553             
12554         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12555             
12556             inputblock = {
12557                 cls : 'has-feedback',
12558                 cn :  [
12559                     input,
12560                     feedback
12561                 ] 
12562             };  
12563         }
12564         
12565         if (this.before || this.after) {
12566             
12567             inputblock = {
12568                 cls : 'input-group',
12569                 cn :  [] 
12570             };
12571             
12572             if (this.before && typeof(this.before) == 'string') {
12573                 
12574                 inputblock.cn.push({
12575                     tag :'span',
12576                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12577                     html : this.before
12578                 });
12579             }
12580             if (this.before && typeof(this.before) == 'object') {
12581                 this.before = Roo.factory(this.before);
12582                 
12583                 inputblock.cn.push({
12584                     tag :'span',
12585                     cls : 'roo-input-before input-group-prepend   input-group-' +
12586                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12587                 });
12588             }
12589             
12590             inputblock.cn.push(input);
12591             
12592             if (this.after && typeof(this.after) == 'string') {
12593                 inputblock.cn.push({
12594                     tag :'span',
12595                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12596                     html : this.after
12597                 });
12598             }
12599             if (this.after && typeof(this.after) == 'object') {
12600                 this.after = Roo.factory(this.after);
12601                 
12602                 inputblock.cn.push({
12603                     tag :'span',
12604                     cls : 'roo-input-after input-group-append  input-group-' +
12605                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12606                 });
12607             }
12608             
12609             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12610                 inputblock.cls += ' has-feedback';
12611                 inputblock.cn.push(feedback);
12612             }
12613         };
12614         var indicator = {
12615             tag : 'i',
12616             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12617             tooltip : 'This field is required'
12618         };
12619         if (this.allowBlank ) {
12620             indicator.style = this.allowBlank ? ' display:none' : '';
12621         }
12622         if (align ==='left' && this.fieldLabel.length) {
12623             
12624             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12625             
12626             cfg.cn = [
12627                 indicator,
12628                 {
12629                     tag: 'label',
12630                     'for' :  id,
12631                     cls : 'control-label col-form-label',
12632                     html : this.fieldLabel
12633
12634                 },
12635                 {
12636                     cls : "", 
12637                     cn: [
12638                         inputblock
12639                     ]
12640                 }
12641             ];
12642             
12643             var labelCfg = cfg.cn[1];
12644             var contentCfg = cfg.cn[2];
12645             
12646             if(this.indicatorpos == 'right'){
12647                 cfg.cn = [
12648                     {
12649                         tag: 'label',
12650                         'for' :  id,
12651                         cls : 'control-label col-form-label',
12652                         cn : [
12653                             {
12654                                 tag : 'span',
12655                                 html : this.fieldLabel
12656                             },
12657                             indicator
12658                         ]
12659                     },
12660                     {
12661                         cls : "",
12662                         cn: [
12663                             inputblock
12664                         ]
12665                     }
12666
12667                 ];
12668                 
12669                 labelCfg = cfg.cn[0];
12670                 contentCfg = cfg.cn[1];
12671             
12672             }
12673             
12674             if(this.labelWidth > 12){
12675                 labelCfg.style = "width: " + this.labelWidth + 'px';
12676             }
12677             
12678             if(this.labelWidth < 13 && this.labelmd == 0){
12679                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12680             }
12681             
12682             if(this.labellg > 0){
12683                 labelCfg.cls += ' col-lg-' + this.labellg;
12684                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12685             }
12686             
12687             if(this.labelmd > 0){
12688                 labelCfg.cls += ' col-md-' + this.labelmd;
12689                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12690             }
12691             
12692             if(this.labelsm > 0){
12693                 labelCfg.cls += ' col-sm-' + this.labelsm;
12694                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12695             }
12696             
12697             if(this.labelxs > 0){
12698                 labelCfg.cls += ' col-xs-' + this.labelxs;
12699                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12700             }
12701             
12702             
12703         } else if ( this.fieldLabel.length) {
12704                 
12705             
12706             
12707             cfg.cn = [
12708                 {
12709                     tag : 'i',
12710                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12711                     tooltip : 'This field is required',
12712                     style : this.allowBlank ? ' display:none' : '' 
12713                 },
12714                 {
12715                     tag: 'label',
12716                    //cls : 'input-group-addon',
12717                     html : this.fieldLabel
12718
12719                 },
12720
12721                inputblock
12722
12723            ];
12724            
12725            if(this.indicatorpos == 'right'){
12726        
12727                 cfg.cn = [
12728                     {
12729                         tag: 'label',
12730                        //cls : 'input-group-addon',
12731                         html : this.fieldLabel
12732
12733                     },
12734                     {
12735                         tag : 'i',
12736                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12737                         tooltip : 'This field is required',
12738                         style : this.allowBlank ? ' display:none' : '' 
12739                     },
12740
12741                    inputblock
12742
12743                ];
12744
12745             }
12746
12747         } else {
12748             
12749             cfg.cn = [
12750
12751                     inputblock
12752
12753             ];
12754                 
12755                 
12756         };
12757         
12758         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12759            cfg.cls += ' navbar-form';
12760         }
12761         
12762         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12763             // on BS4 we do this only if not form 
12764             cfg.cls += ' navbar-form';
12765             cfg.tag = 'li';
12766         }
12767         
12768         return cfg;
12769         
12770     },
12771     /**
12772      * return the real input element.
12773      */
12774     inputEl: function ()
12775     {
12776         return this.el.select('input.form-control',true).first();
12777     },
12778     
12779     tooltipEl : function()
12780     {
12781         return this.inputEl();
12782     },
12783     
12784     indicatorEl : function()
12785     {
12786         if (Roo.bootstrap.version == 4) {
12787             return false; // not enabled in v4 yet.
12788         }
12789         
12790         var indicator = this.el.select('i.roo-required-indicator',true).first();
12791         
12792         if(!indicator){
12793             return false;
12794         }
12795         
12796         return indicator;
12797         
12798     },
12799     
12800     setDisabled : function(v)
12801     {
12802         var i  = this.inputEl().dom;
12803         if (!v) {
12804             i.removeAttribute('disabled');
12805             return;
12806             
12807         }
12808         i.setAttribute('disabled','true');
12809     },
12810     initEvents : function()
12811     {
12812           
12813         this.inputEl().on("keydown" , this.fireKey,  this);
12814         this.inputEl().on("focus", this.onFocus,  this);
12815         this.inputEl().on("blur", this.onBlur,  this);
12816         
12817         this.inputEl().relayEvent('keyup', this);
12818         this.inputEl().relayEvent('paste', this);
12819         
12820         this.indicator = this.indicatorEl();
12821         
12822         if(this.indicator){
12823             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12824         }
12825  
12826         // reference to original value for reset
12827         this.originalValue = this.getValue();
12828         //Roo.form.TextField.superclass.initEvents.call(this);
12829         if(this.validationEvent == 'keyup'){
12830             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12831             this.inputEl().on('keyup', this.filterValidation, this);
12832         }
12833         else if(this.validationEvent !== false){
12834             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12835         }
12836         
12837         if(this.selectOnFocus){
12838             this.on("focus", this.preFocus, this);
12839             
12840         }
12841         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12842             this.inputEl().on("keypress", this.filterKeys, this);
12843         } else {
12844             this.inputEl().relayEvent('keypress', this);
12845         }
12846        /* if(this.grow){
12847             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12848             this.el.on("click", this.autoSize,  this);
12849         }
12850         */
12851         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12852             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12853         }
12854         
12855         if (typeof(this.before) == 'object') {
12856             this.before.render(this.el.select('.roo-input-before',true).first());
12857         }
12858         if (typeof(this.after) == 'object') {
12859             this.after.render(this.el.select('.roo-input-after',true).first());
12860         }
12861         
12862         this.inputEl().on('change', this.onChange, this);
12863         
12864     },
12865     filterValidation : function(e){
12866         if(!e.isNavKeyPress()){
12867             this.validationTask.delay(this.validationDelay);
12868         }
12869     },
12870      /**
12871      * Validates the field value
12872      * @return {Boolean} True if the value is valid, else false
12873      */
12874     validate : function(){
12875         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12876         if(this.disabled || this.validateValue(this.getRawValue())){
12877             this.markValid();
12878             return true;
12879         }
12880         
12881         this.markInvalid();
12882         return false;
12883     },
12884     
12885     
12886     /**
12887      * Validates a value according to the field's validation rules and marks the field as invalid
12888      * if the validation fails
12889      * @param {Mixed} value The value to validate
12890      * @return {Boolean} True if the value is valid, else false
12891      */
12892     validateValue : function(value)
12893     {
12894         if(this.getVisibilityEl().hasClass('hidden')){
12895             return true;
12896         }
12897         
12898         if(value.length < 1)  { // if it's blank
12899             if(this.allowBlank){
12900                 return true;
12901             }
12902             return false;
12903         }
12904         
12905         if(value.length < this.minLength){
12906             return false;
12907         }
12908         if(value.length > this.maxLength){
12909             return false;
12910         }
12911         if(this.vtype){
12912             var vt = Roo.form.VTypes;
12913             if(!vt[this.vtype](value, this)){
12914                 return false;
12915             }
12916         }
12917         if(typeof this.validator == "function"){
12918             var msg = this.validator(value);
12919             if(msg !== true){
12920                 return false;
12921             }
12922             if (typeof(msg) == 'string') {
12923                 this.invalidText = msg;
12924             }
12925         }
12926         
12927         if(this.regex && !this.regex.test(value)){
12928             return false;
12929         }
12930         
12931         return true;
12932     },
12933     
12934      // private
12935     fireKey : function(e){
12936         //Roo.log('field ' + e.getKey());
12937         if(e.isNavKeyPress()){
12938             this.fireEvent("specialkey", this, e);
12939         }
12940     },
12941     focus : function (selectText){
12942         if(this.rendered){
12943             this.inputEl().focus();
12944             if(selectText === true){
12945                 this.inputEl().dom.select();
12946             }
12947         }
12948         return this;
12949     } ,
12950     
12951     onFocus : function(){
12952         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12953            // this.el.addClass(this.focusClass);
12954         }
12955         if(!this.hasFocus){
12956             this.hasFocus = true;
12957             this.startValue = this.getValue();
12958             this.fireEvent("focus", this);
12959         }
12960     },
12961     
12962     beforeBlur : Roo.emptyFn,
12963
12964     
12965     // private
12966     onBlur : function(){
12967         this.beforeBlur();
12968         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12969             //this.el.removeClass(this.focusClass);
12970         }
12971         this.hasFocus = false;
12972         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12973             this.validate();
12974         }
12975         var v = this.getValue();
12976         if(String(v) !== String(this.startValue)){
12977             this.fireEvent('change', this, v, this.startValue);
12978         }
12979         this.fireEvent("blur", this);
12980     },
12981     
12982     onChange : function(e)
12983     {
12984         var v = this.getValue();
12985         if(String(v) !== String(this.startValue)){
12986             this.fireEvent('change', this, v, this.startValue);
12987         }
12988         
12989     },
12990     
12991     /**
12992      * Resets the current field value to the originally loaded value and clears any validation messages
12993      */
12994     reset : function(){
12995         this.setValue(this.originalValue);
12996         this.validate();
12997     },
12998      /**
12999      * Returns the name of the field
13000      * @return {Mixed} name The name field
13001      */
13002     getName: function(){
13003         return this.name;
13004     },
13005      /**
13006      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13007      * @return {Mixed} value The field value
13008      */
13009     getValue : function(){
13010         
13011         var v = this.inputEl().getValue();
13012         
13013         return v;
13014     },
13015     /**
13016      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13017      * @return {Mixed} value The field value
13018      */
13019     getRawValue : function(){
13020         var v = this.inputEl().getValue();
13021         
13022         return v;
13023     },
13024     
13025     /**
13026      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13027      * @param {Mixed} value The value to set
13028      */
13029     setRawValue : function(v){
13030         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13031     },
13032     
13033     selectText : function(start, end){
13034         var v = this.getRawValue();
13035         if(v.length > 0){
13036             start = start === undefined ? 0 : start;
13037             end = end === undefined ? v.length : end;
13038             var d = this.inputEl().dom;
13039             if(d.setSelectionRange){
13040                 d.setSelectionRange(start, end);
13041             }else if(d.createTextRange){
13042                 var range = d.createTextRange();
13043                 range.moveStart("character", start);
13044                 range.moveEnd("character", v.length-end);
13045                 range.select();
13046             }
13047         }
13048     },
13049     
13050     /**
13051      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13052      * @param {Mixed} value The value to set
13053      */
13054     setValue : function(v){
13055         this.value = v;
13056         if(this.rendered){
13057             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13058             this.validate();
13059         }
13060     },
13061     
13062     /*
13063     processValue : function(value){
13064         if(this.stripCharsRe){
13065             var newValue = value.replace(this.stripCharsRe, '');
13066             if(newValue !== value){
13067                 this.setRawValue(newValue);
13068                 return newValue;
13069             }
13070         }
13071         return value;
13072     },
13073   */
13074     preFocus : function(){
13075         
13076         if(this.selectOnFocus){
13077             this.inputEl().dom.select();
13078         }
13079     },
13080     filterKeys : function(e){
13081         var k = e.getKey();
13082         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13083             return;
13084         }
13085         var c = e.getCharCode(), cc = String.fromCharCode(c);
13086         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13087             return;
13088         }
13089         if(!this.maskRe.test(cc)){
13090             e.stopEvent();
13091         }
13092     },
13093      /**
13094      * Clear any invalid styles/messages for this field
13095      */
13096     clearInvalid : function(){
13097         
13098         if(!this.el || this.preventMark){ // not rendered
13099             return;
13100         }
13101         
13102         
13103         this.el.removeClass([this.invalidClass, 'is-invalid']);
13104         
13105         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13106             
13107             var feedback = this.el.select('.form-control-feedback', true).first();
13108             
13109             if(feedback){
13110                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13111             }
13112             
13113         }
13114         
13115         if(this.indicator){
13116             this.indicator.removeClass('visible');
13117             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13118         }
13119         
13120         this.fireEvent('valid', this);
13121     },
13122     
13123      /**
13124      * Mark this field as valid
13125      */
13126     markValid : function()
13127     {
13128         if(!this.el  || this.preventMark){ // not rendered...
13129             return;
13130         }
13131         
13132         this.el.removeClass([this.invalidClass, this.validClass]);
13133         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13134
13135         var feedback = this.el.select('.form-control-feedback', true).first();
13136             
13137         if(feedback){
13138             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13139         }
13140         
13141         if(this.indicator){
13142             this.indicator.removeClass('visible');
13143             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13144         }
13145         
13146         if(this.disabled){
13147             return;
13148         }
13149         
13150            
13151         if(this.allowBlank && !this.getRawValue().length){
13152             return;
13153         }
13154         if (Roo.bootstrap.version == 3) {
13155             this.el.addClass(this.validClass);
13156         } else {
13157             this.inputEl().addClass('is-valid');
13158         }
13159
13160         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13161             
13162             var feedback = this.el.select('.form-control-feedback', true).first();
13163             
13164             if(feedback){
13165                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13166                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13167             }
13168             
13169         }
13170         
13171         this.fireEvent('valid', this);
13172     },
13173     
13174      /**
13175      * Mark this field as invalid
13176      * @param {String} msg The validation message
13177      */
13178     markInvalid : function(msg)
13179     {
13180         if(!this.el  || this.preventMark){ // not rendered
13181             return;
13182         }
13183         
13184         this.el.removeClass([this.invalidClass, this.validClass]);
13185         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13186         
13187         var feedback = this.el.select('.form-control-feedback', true).first();
13188             
13189         if(feedback){
13190             this.el.select('.form-control-feedback', true).first().removeClass(
13191                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13192         }
13193
13194         if(this.disabled){
13195             return;
13196         }
13197         
13198         if(this.allowBlank && !this.getRawValue().length){
13199             return;
13200         }
13201         
13202         if(this.indicator){
13203             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13204             this.indicator.addClass('visible');
13205         }
13206         if (Roo.bootstrap.version == 3) {
13207             this.el.addClass(this.invalidClass);
13208         } else {
13209             this.inputEl().addClass('is-invalid');
13210         }
13211         
13212         
13213         
13214         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13215             
13216             var feedback = this.el.select('.form-control-feedback', true).first();
13217             
13218             if(feedback){
13219                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13220                 
13221                 if(this.getValue().length || this.forceFeedback){
13222                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13223                 }
13224                 
13225             }
13226             
13227         }
13228         
13229         this.fireEvent('invalid', this, msg);
13230     },
13231     // private
13232     SafariOnKeyDown : function(event)
13233     {
13234         // this is a workaround for a password hang bug on chrome/ webkit.
13235         if (this.inputEl().dom.type != 'password') {
13236             return;
13237         }
13238         
13239         var isSelectAll = false;
13240         
13241         if(this.inputEl().dom.selectionEnd > 0){
13242             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13243         }
13244         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13245             event.preventDefault();
13246             this.setValue('');
13247             return;
13248         }
13249         
13250         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13251             
13252             event.preventDefault();
13253             // this is very hacky as keydown always get's upper case.
13254             //
13255             var cc = String.fromCharCode(event.getCharCode());
13256             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13257             
13258         }
13259     },
13260     adjustWidth : function(tag, w){
13261         tag = tag.toLowerCase();
13262         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13263             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13264                 if(tag == 'input'){
13265                     return w + 2;
13266                 }
13267                 if(tag == 'textarea'){
13268                     return w-2;
13269                 }
13270             }else if(Roo.isOpera){
13271                 if(tag == 'input'){
13272                     return w + 2;
13273                 }
13274                 if(tag == 'textarea'){
13275                     return w-2;
13276                 }
13277             }
13278         }
13279         return w;
13280     },
13281     
13282     setFieldLabel : function(v)
13283     {
13284         if(!this.rendered){
13285             return;
13286         }
13287         
13288         if(this.indicatorEl()){
13289             var ar = this.el.select('label > span',true);
13290             
13291             if (ar.elements.length) {
13292                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13293                 this.fieldLabel = v;
13294                 return;
13295             }
13296             
13297             var br = this.el.select('label',true);
13298             
13299             if(br.elements.length) {
13300                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13301                 this.fieldLabel = v;
13302                 return;
13303             }
13304             
13305             Roo.log('Cannot Found any of label > span || label in input');
13306             return;
13307         }
13308         
13309         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13310         this.fieldLabel = v;
13311         
13312         
13313     }
13314 });
13315
13316  
13317 /*
13318  * - LGPL
13319  *
13320  * Input
13321  * 
13322  */
13323
13324 /**
13325  * @class Roo.bootstrap.form.TextArea
13326  * @extends Roo.bootstrap.form.Input
13327  * Bootstrap TextArea class
13328  * @cfg {Number} cols Specifies the visible width of a text area
13329  * @cfg {Number} rows Specifies the visible number of lines in a text area
13330  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13331  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13332  * @cfg {string} html text
13333  * 
13334  * @constructor
13335  * Create a new TextArea
13336  * @param {Object} config The config object
13337  */
13338
13339 Roo.bootstrap.form.TextArea = function(config){
13340     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13341    
13342 };
13343
13344 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13345      
13346     cols : false,
13347     rows : 5,
13348     readOnly : false,
13349     warp : 'soft',
13350     resize : false,
13351     value: false,
13352     html: false,
13353     
13354     getAutoCreate : function(){
13355         
13356         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13357         
13358         var id = Roo.id();
13359         
13360         var cfg = {};
13361         
13362         if(this.inputType != 'hidden'){
13363             cfg.cls = 'form-group' //input-group
13364         }
13365         
13366         var input =  {
13367             tag: 'textarea',
13368             id : id,
13369             warp : this.warp,
13370             rows : this.rows,
13371             value : this.value || '',
13372             html: this.html || '',
13373             cls : 'form-control',
13374             placeholder : this.placeholder || '' 
13375             
13376         };
13377         
13378         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13379             input.maxLength = this.maxLength;
13380         }
13381         
13382         if(this.resize){
13383             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13384         }
13385         
13386         if(this.cols){
13387             input.cols = this.cols;
13388         }
13389         
13390         if (this.readOnly) {
13391             input.readonly = true;
13392         }
13393         
13394         if (this.name) {
13395             input.name = this.name;
13396         }
13397         
13398         if (this.size) {
13399             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13400         }
13401         
13402         var settings=this;
13403         ['xs','sm','md','lg'].map(function(size){
13404             if (settings[size]) {
13405                 cfg.cls += ' col-' + size + '-' + settings[size];
13406             }
13407         });
13408         
13409         var inputblock = input;
13410         
13411         if(this.hasFeedback && !this.allowBlank){
13412             
13413             var feedback = {
13414                 tag: 'span',
13415                 cls: 'glyphicon form-control-feedback'
13416             };
13417
13418             inputblock = {
13419                 cls : 'has-feedback',
13420                 cn :  [
13421                     input,
13422                     feedback
13423                 ] 
13424             };  
13425         }
13426         
13427         
13428         if (this.before || this.after) {
13429             
13430             inputblock = {
13431                 cls : 'input-group',
13432                 cn :  [] 
13433             };
13434             if (this.before) {
13435                 inputblock.cn.push({
13436                     tag :'span',
13437                     cls : 'input-group-addon',
13438                     html : this.before
13439                 });
13440             }
13441             
13442             inputblock.cn.push(input);
13443             
13444             if(this.hasFeedback && !this.allowBlank){
13445                 inputblock.cls += ' has-feedback';
13446                 inputblock.cn.push(feedback);
13447             }
13448             
13449             if (this.after) {
13450                 inputblock.cn.push({
13451                     tag :'span',
13452                     cls : 'input-group-addon',
13453                     html : this.after
13454                 });
13455             }
13456             
13457         }
13458         
13459         if (align ==='left' && this.fieldLabel.length) {
13460             cfg.cn = [
13461                 {
13462                     tag: 'label',
13463                     'for' :  id,
13464                     cls : 'control-label',
13465                     html : this.fieldLabel
13466                 },
13467                 {
13468                     cls : "",
13469                     cn: [
13470                         inputblock
13471                     ]
13472                 }
13473
13474             ];
13475             
13476             if(this.labelWidth > 12){
13477                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13478             }
13479
13480             if(this.labelWidth < 13 && this.labelmd == 0){
13481                 this.labelmd = this.labelWidth;
13482             }
13483
13484             if(this.labellg > 0){
13485                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13486                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13487             }
13488
13489             if(this.labelmd > 0){
13490                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13491                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13492             }
13493
13494             if(this.labelsm > 0){
13495                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13496                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13497             }
13498
13499             if(this.labelxs > 0){
13500                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13501                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13502             }
13503             
13504         } else if ( this.fieldLabel.length) {
13505             cfg.cn = [
13506
13507                {
13508                    tag: 'label',
13509                    //cls : 'input-group-addon',
13510                    html : this.fieldLabel
13511
13512                },
13513
13514                inputblock
13515
13516            ];
13517
13518         } else {
13519
13520             cfg.cn = [
13521
13522                 inputblock
13523
13524             ];
13525                 
13526         }
13527         
13528         if (this.disabled) {
13529             input.disabled=true;
13530         }
13531         
13532         return cfg;
13533         
13534     },
13535     /**
13536      * return the real textarea element.
13537      */
13538     inputEl: function ()
13539     {
13540         return this.el.select('textarea.form-control',true).first();
13541     },
13542     
13543     /**
13544      * Clear any invalid styles/messages for this field
13545      */
13546     clearInvalid : function()
13547     {
13548         
13549         if(!this.el || this.preventMark){ // not rendered
13550             return;
13551         }
13552         
13553         var label = this.el.select('label', true).first();
13554         var icon = this.el.select('i.fa-star', true).first();
13555         
13556         if(label && icon){
13557             icon.remove();
13558         }
13559         this.el.removeClass( this.validClass);
13560         this.inputEl().removeClass('is-invalid');
13561          
13562         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13563             
13564             var feedback = this.el.select('.form-control-feedback', true).first();
13565             
13566             if(feedback){
13567                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13568             }
13569             
13570         }
13571         
13572         this.fireEvent('valid', this);
13573     },
13574     
13575      /**
13576      * Mark this field as valid
13577      */
13578     markValid : function()
13579     {
13580         if(!this.el  || this.preventMark){ // not rendered
13581             return;
13582         }
13583         
13584         this.el.removeClass([this.invalidClass, this.validClass]);
13585         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13586         
13587         var feedback = this.el.select('.form-control-feedback', true).first();
13588             
13589         if(feedback){
13590             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13591         }
13592
13593         if(this.disabled || this.allowBlank){
13594             return;
13595         }
13596         
13597         var label = this.el.select('label', true).first();
13598         var icon = this.el.select('i.fa-star', true).first();
13599         
13600         if(label && icon){
13601             icon.remove();
13602         }
13603         if (Roo.bootstrap.version == 3) {
13604             this.el.addClass(this.validClass);
13605         } else {
13606             this.inputEl().addClass('is-valid');
13607         }
13608         
13609         
13610         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13611             
13612             var feedback = this.el.select('.form-control-feedback', true).first();
13613             
13614             if(feedback){
13615                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13616                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13617             }
13618             
13619         }
13620         
13621         this.fireEvent('valid', this);
13622     },
13623     
13624      /**
13625      * Mark this field as invalid
13626      * @param {String} msg The validation message
13627      */
13628     markInvalid : function(msg)
13629     {
13630         if(!this.el  || this.preventMark){ // not rendered
13631             return;
13632         }
13633         
13634         this.el.removeClass([this.invalidClass, this.validClass]);
13635         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13636         
13637         var feedback = this.el.select('.form-control-feedback', true).first();
13638             
13639         if(feedback){
13640             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13641         }
13642
13643         if(this.disabled || this.allowBlank){
13644             return;
13645         }
13646         
13647         var label = this.el.select('label', true).first();
13648         var icon = this.el.select('i.fa-star', true).first();
13649         
13650         if(!this.getValue().length && label && !icon){
13651             this.el.createChild({
13652                 tag : 'i',
13653                 cls : 'text-danger fa fa-lg fa-star',
13654                 tooltip : 'This field is required',
13655                 style : 'margin-right:5px;'
13656             }, label, true);
13657         }
13658         
13659         if (Roo.bootstrap.version == 3) {
13660             this.el.addClass(this.invalidClass);
13661         } else {
13662             this.inputEl().addClass('is-invalid');
13663         }
13664         
13665         // fixme ... this may be depricated need to test..
13666         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13667             
13668             var feedback = this.el.select('.form-control-feedback', true).first();
13669             
13670             if(feedback){
13671                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13672                 
13673                 if(this.getValue().length || this.forceFeedback){
13674                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13675                 }
13676                 
13677             }
13678             
13679         }
13680         
13681         this.fireEvent('invalid', this, msg);
13682     }
13683 });
13684
13685  
13686 /*
13687  * - LGPL
13688  *
13689  * trigger field - base class for combo..
13690  * 
13691  */
13692  
13693 /**
13694  * @class Roo.bootstrap.form.TriggerField
13695  * @extends Roo.bootstrap.form.Input
13696  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13697  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13698  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13699  * for which you can provide a custom implementation.  For example:
13700  * <pre><code>
13701 var trigger = new Roo.bootstrap.form.TriggerField();
13702 trigger.onTriggerClick = myTriggerFn;
13703 trigger.applyTo('my-field');
13704 </code></pre>
13705  *
13706  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13707  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13708  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13709  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13710  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13711
13712  * @constructor
13713  * Create a new TriggerField.
13714  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13715  * to the base TextField)
13716  */
13717 Roo.bootstrap.form.TriggerField = function(config){
13718     this.mimicing = false;
13719     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13720 };
13721
13722 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13723     /**
13724      * @cfg {String} triggerClass A CSS class to apply to the trigger
13725      */
13726      /**
13727      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13728      */
13729     hideTrigger:false,
13730
13731     /**
13732      * @cfg {Boolean} removable (true|false) special filter default false
13733      */
13734     removable : false,
13735     
13736     /** @cfg {Boolean} grow @hide */
13737     /** @cfg {Number} growMin @hide */
13738     /** @cfg {Number} growMax @hide */
13739
13740     /**
13741      * @hide 
13742      * @method
13743      */
13744     autoSize: Roo.emptyFn,
13745     // private
13746     monitorTab : true,
13747     // private
13748     deferHeight : true,
13749
13750     
13751     actionMode : 'wrap',
13752     
13753     caret : false,
13754     
13755     
13756     getAutoCreate : function(){
13757        
13758         var align = this.labelAlign || this.parentLabelAlign();
13759         
13760         var id = Roo.id();
13761         
13762         var cfg = {
13763             cls: 'form-group' //input-group
13764         };
13765         
13766         
13767         var input =  {
13768             tag: 'input',
13769             id : id,
13770             type : this.inputType,
13771             cls : 'form-control',
13772             autocomplete: 'new-password',
13773             placeholder : this.placeholder || '' 
13774             
13775         };
13776         if (this.name) {
13777             input.name = this.name;
13778         }
13779         if (this.size) {
13780             input.cls += ' input-' + this.size;
13781         }
13782         
13783         if (this.disabled) {
13784             input.disabled=true;
13785         }
13786         
13787         var inputblock = input;
13788         
13789         if(this.hasFeedback && !this.allowBlank){
13790             
13791             var feedback = {
13792                 tag: 'span',
13793                 cls: 'glyphicon form-control-feedback'
13794             };
13795             
13796             if(this.removable && !this.editable  ){
13797                 inputblock = {
13798                     cls : 'has-feedback',
13799                     cn :  [
13800                         inputblock,
13801                         {
13802                             tag: 'button',
13803                             html : 'x',
13804                             cls : 'roo-combo-removable-btn close'
13805                         },
13806                         feedback
13807                     ] 
13808                 };
13809             } else {
13810                 inputblock = {
13811                     cls : 'has-feedback',
13812                     cn :  [
13813                         inputblock,
13814                         feedback
13815                     ] 
13816                 };
13817             }
13818
13819         } else {
13820             if(this.removable && !this.editable ){
13821                 inputblock = {
13822                     cls : 'roo-removable',
13823                     cn :  [
13824                         inputblock,
13825                         {
13826                             tag: 'button',
13827                             html : 'x',
13828                             cls : 'roo-combo-removable-btn close'
13829                         }
13830                     ] 
13831                 };
13832             }
13833         }
13834         
13835         if (this.before || this.after) {
13836             
13837             inputblock = {
13838                 cls : 'input-group',
13839                 cn :  [] 
13840             };
13841             if (this.before) {
13842                 inputblock.cn.push({
13843                     tag :'span',
13844                     cls : 'input-group-addon input-group-prepend input-group-text',
13845                     html : this.before
13846                 });
13847             }
13848             
13849             inputblock.cn.push(input);
13850             
13851             if(this.hasFeedback && !this.allowBlank){
13852                 inputblock.cls += ' has-feedback';
13853                 inputblock.cn.push(feedback);
13854             }
13855             
13856             if (this.after) {
13857                 inputblock.cn.push({
13858                     tag :'span',
13859                     cls : 'input-group-addon input-group-append input-group-text',
13860                     html : this.after
13861                 });
13862             }
13863             
13864         };
13865         
13866       
13867         
13868         var ibwrap = inputblock;
13869         
13870         if(this.multiple){
13871             ibwrap = {
13872                 tag: 'ul',
13873                 cls: 'roo-select2-choices',
13874                 cn:[
13875                     {
13876                         tag: 'li',
13877                         cls: 'roo-select2-search-field',
13878                         cn: [
13879
13880                             inputblock
13881                         ]
13882                     }
13883                 ]
13884             };
13885                 
13886         }
13887         
13888         var combobox = {
13889             cls: 'roo-select2-container input-group',
13890             cn: [
13891                  {
13892                     tag: 'input',
13893                     type : 'hidden',
13894                     cls: 'form-hidden-field'
13895                 },
13896                 ibwrap
13897             ]
13898         };
13899         
13900         if(!this.multiple && this.showToggleBtn){
13901             
13902             var caret = {
13903                         tag: 'span',
13904                         cls: 'caret'
13905              };
13906             if (this.caret != false) {
13907                 caret = {
13908                      tag: 'i',
13909                      cls: 'fa fa-' + this.caret
13910                 };
13911                 
13912             }
13913             
13914             combobox.cn.push({
13915                 tag :'span',
13916                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13917                 cn : [
13918                     Roo.bootstrap.version == 3 ? caret : '',
13919                     {
13920                         tag: 'span',
13921                         cls: 'combobox-clear',
13922                         cn  : [
13923                             {
13924                                 tag : 'i',
13925                                 cls: 'icon-remove'
13926                             }
13927                         ]
13928                     }
13929                 ]
13930
13931             })
13932         }
13933         
13934         if(this.multiple){
13935             combobox.cls += ' roo-select2-container-multi';
13936         }
13937          var indicator = {
13938             tag : 'i',
13939             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13940             tooltip : 'This field is required'
13941         };
13942         if (Roo.bootstrap.version == 4) {
13943             indicator = {
13944                 tag : 'i',
13945                 style : 'display:none'
13946             };
13947         }
13948         
13949         
13950         if (align ==='left' && this.fieldLabel.length) {
13951             
13952             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13953
13954             cfg.cn = [
13955                 indicator,
13956                 {
13957                     tag: 'label',
13958                     'for' :  id,
13959                     cls : 'control-label',
13960                     html : this.fieldLabel
13961
13962                 },
13963                 {
13964                     cls : "", 
13965                     cn: [
13966                         combobox
13967                     ]
13968                 }
13969
13970             ];
13971             
13972             var labelCfg = cfg.cn[1];
13973             var contentCfg = cfg.cn[2];
13974             
13975             if(this.indicatorpos == 'right'){
13976                 cfg.cn = [
13977                     {
13978                         tag: 'label',
13979                         'for' :  id,
13980                         cls : 'control-label',
13981                         cn : [
13982                             {
13983                                 tag : 'span',
13984                                 html : this.fieldLabel
13985                             },
13986                             indicator
13987                         ]
13988                     },
13989                     {
13990                         cls : "", 
13991                         cn: [
13992                             combobox
13993                         ]
13994                     }
13995
13996                 ];
13997                 
13998                 labelCfg = cfg.cn[0];
13999                 contentCfg = cfg.cn[1];
14000             }
14001             
14002             if(this.labelWidth > 12){
14003                 labelCfg.style = "width: " + this.labelWidth + 'px';
14004             }
14005             
14006             if(this.labelWidth < 13 && this.labelmd == 0){
14007                 this.labelmd = this.labelWidth;
14008             }
14009             
14010             if(this.labellg > 0){
14011                 labelCfg.cls += ' col-lg-' + this.labellg;
14012                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14013             }
14014             
14015             if(this.labelmd > 0){
14016                 labelCfg.cls += ' col-md-' + this.labelmd;
14017                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14018             }
14019             
14020             if(this.labelsm > 0){
14021                 labelCfg.cls += ' col-sm-' + this.labelsm;
14022                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14023             }
14024             
14025             if(this.labelxs > 0){
14026                 labelCfg.cls += ' col-xs-' + this.labelxs;
14027                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14028             }
14029             
14030         } else if ( this.fieldLabel.length) {
14031 //                Roo.log(" label");
14032             cfg.cn = [
14033                 indicator,
14034                {
14035                    tag: 'label',
14036                    //cls : 'input-group-addon',
14037                    html : this.fieldLabel
14038
14039                },
14040
14041                combobox
14042
14043             ];
14044             
14045             if(this.indicatorpos == 'right'){
14046                 
14047                 cfg.cn = [
14048                     {
14049                        tag: 'label',
14050                        cn : [
14051                            {
14052                                tag : 'span',
14053                                html : this.fieldLabel
14054                            },
14055                            indicator
14056                        ]
14057
14058                     },
14059                     combobox
14060
14061                 ];
14062
14063             }
14064
14065         } else {
14066             
14067 //                Roo.log(" no label && no align");
14068                 cfg = combobox
14069                      
14070                 
14071         }
14072         
14073         var settings=this;
14074         ['xs','sm','md','lg'].map(function(size){
14075             if (settings[size]) {
14076                 cfg.cls += ' col-' + size + '-' + settings[size];
14077             }
14078         });
14079         
14080         return cfg;
14081         
14082     },
14083     
14084     
14085     
14086     // private
14087     onResize : function(w, h){
14088 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14089 //        if(typeof w == 'number'){
14090 //            var x = w - this.trigger.getWidth();
14091 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14092 //            this.trigger.setStyle('left', x+'px');
14093 //        }
14094     },
14095
14096     // private
14097     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14098
14099     // private
14100     getResizeEl : function(){
14101         return this.inputEl();
14102     },
14103
14104     // private
14105     getPositionEl : function(){
14106         return this.inputEl();
14107     },
14108
14109     // private
14110     alignErrorIcon : function(){
14111         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14112     },
14113
14114     // private
14115     initEvents : function(){
14116         
14117         this.createList();
14118         
14119         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14120         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14121         if(!this.multiple && this.showToggleBtn){
14122             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14123             if(this.hideTrigger){
14124                 this.trigger.setDisplayed(false);
14125             }
14126             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14127         }
14128         
14129         if(this.multiple){
14130             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14131         }
14132         
14133         if(this.removable && !this.editable && !this.tickable){
14134             var close = this.closeTriggerEl();
14135             
14136             if(close){
14137                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14138                 close.on('click', this.removeBtnClick, this, close);
14139             }
14140         }
14141         
14142         //this.trigger.addClassOnOver('x-form-trigger-over');
14143         //this.trigger.addClassOnClick('x-form-trigger-click');
14144         
14145         //if(!this.width){
14146         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14147         //}
14148     },
14149     
14150     closeTriggerEl : function()
14151     {
14152         var close = this.el.select('.roo-combo-removable-btn', true).first();
14153         return close ? close : false;
14154     },
14155     
14156     removeBtnClick : function(e, h, el)
14157     {
14158         e.preventDefault();
14159         
14160         if(this.fireEvent("remove", this) !== false){
14161             this.reset();
14162             this.fireEvent("afterremove", this)
14163         }
14164     },
14165     
14166     createList : function()
14167     {
14168         this.list = Roo.get(document.body).createChild({
14169             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14170             cls: 'typeahead typeahead-long dropdown-menu shadow',
14171             style: 'display:none'
14172         });
14173         
14174         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14175         
14176     },
14177
14178     // private
14179     initTrigger : function(){
14180        
14181     },
14182
14183     // private
14184     onDestroy : function(){
14185         if(this.trigger){
14186             this.trigger.removeAllListeners();
14187           //  this.trigger.remove();
14188         }
14189         //if(this.wrap){
14190         //    this.wrap.remove();
14191         //}
14192         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14193     },
14194
14195     // private
14196     onFocus : function(){
14197         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14198         /*
14199         if(!this.mimicing){
14200             this.wrap.addClass('x-trigger-wrap-focus');
14201             this.mimicing = true;
14202             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14203             if(this.monitorTab){
14204                 this.el.on("keydown", this.checkTab, this);
14205             }
14206         }
14207         */
14208     },
14209
14210     // private
14211     checkTab : function(e){
14212         if(e.getKey() == e.TAB){
14213             this.triggerBlur();
14214         }
14215     },
14216
14217     // private
14218     onBlur : function(){
14219         // do nothing
14220     },
14221
14222     // private
14223     mimicBlur : function(e, t){
14224         /*
14225         if(!this.wrap.contains(t) && this.validateBlur()){
14226             this.triggerBlur();
14227         }
14228         */
14229     },
14230
14231     // private
14232     triggerBlur : function(){
14233         this.mimicing = false;
14234         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14235         if(this.monitorTab){
14236             this.el.un("keydown", this.checkTab, this);
14237         }
14238         //this.wrap.removeClass('x-trigger-wrap-focus');
14239         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14240     },
14241
14242     // private
14243     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14244     validateBlur : function(e, t){
14245         return true;
14246     },
14247
14248     // private
14249     onDisable : function(){
14250         this.inputEl().dom.disabled = true;
14251         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14252         //if(this.wrap){
14253         //    this.wrap.addClass('x-item-disabled');
14254         //}
14255     },
14256
14257     // private
14258     onEnable : function(){
14259         this.inputEl().dom.disabled = false;
14260         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14261         //if(this.wrap){
14262         //    this.el.removeClass('x-item-disabled');
14263         //}
14264     },
14265
14266     // private
14267     onShow : function(){
14268         var ae = this.getActionEl();
14269         
14270         if(ae){
14271             ae.dom.style.display = '';
14272             ae.dom.style.visibility = 'visible';
14273         }
14274     },
14275
14276     // private
14277     
14278     onHide : function(){
14279         var ae = this.getActionEl();
14280         ae.dom.style.display = 'none';
14281     },
14282
14283     /**
14284      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14285      * by an implementing function.
14286      * @method
14287      * @param {EventObject} e
14288      */
14289     onTriggerClick : Roo.emptyFn
14290 });
14291  
14292 /*
14293 * Licence: LGPL
14294 */
14295
14296 /**
14297  * @class Roo.bootstrap.form.CardUploader
14298  * @extends Roo.bootstrap.Button
14299  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14300  * @cfg {Number} errorTimeout default 3000
14301  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14302  * @cfg {Array}  html The button text.
14303
14304  *
14305  * @constructor
14306  * Create a new CardUploader
14307  * @param {Object} config The config object
14308  */
14309
14310 Roo.bootstrap.form.CardUploader = function(config){
14311     
14312  
14313     
14314     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14315     
14316     
14317     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14318         return r.data.id
14319      });
14320     
14321      this.addEvents({
14322          // raw events
14323         /**
14324          * @event preview
14325          * When a image is clicked on - and needs to display a slideshow or similar..
14326          * @param {Roo.bootstrap.Card} this
14327          * @param {Object} The image information data 
14328          *
14329          */
14330         'preview' : true,
14331          /**
14332          * @event download
14333          * When a the download link is clicked
14334          * @param {Roo.bootstrap.Card} this
14335          * @param {Object} The image information data  contains 
14336          */
14337         'download' : true
14338         
14339     });
14340 };
14341  
14342 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14343     
14344      
14345     errorTimeout : 3000,
14346      
14347     images : false,
14348    
14349     fileCollection : false,
14350     allowBlank : true,
14351     
14352     getAutoCreate : function()
14353     {
14354         
14355         var cfg =  {
14356             cls :'form-group' ,
14357             cn : [
14358                
14359                 {
14360                     tag: 'label',
14361                    //cls : 'input-group-addon',
14362                     html : this.fieldLabel
14363
14364                 },
14365
14366                 {
14367                     tag: 'input',
14368                     type : 'hidden',
14369                     name : this.name,
14370                     value : this.value,
14371                     cls : 'd-none  form-control'
14372                 },
14373                 
14374                 {
14375                     tag: 'input',
14376                     multiple : 'multiple',
14377                     type : 'file',
14378                     cls : 'd-none  roo-card-upload-selector'
14379                 },
14380                 
14381                 {
14382                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14383                 },
14384                 {
14385                     cls : 'card-columns roo-card-uploader-container'
14386                 }
14387
14388             ]
14389         };
14390            
14391          
14392         return cfg;
14393     },
14394     
14395     getChildContainer : function() /// what children are added to.
14396     {
14397         return this.containerEl;
14398     },
14399    
14400     getButtonContainer : function() /// what children are added to.
14401     {
14402         return this.el.select(".roo-card-uploader-button-container").first();
14403     },
14404    
14405     initEvents : function()
14406     {
14407         
14408         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14409         
14410         var t = this;
14411         this.addxtype({
14412             xns: Roo.bootstrap,
14413
14414             xtype : 'Button',
14415             container_method : 'getButtonContainer' ,            
14416             html :  this.html, // fix changable?
14417             cls : 'w-100 ',
14418             listeners : {
14419                 'click' : function(btn, e) {
14420                     t.onClick(e);
14421                 }
14422             }
14423         });
14424         
14425         
14426         
14427         
14428         this.urlAPI = (window.createObjectURL && window) || 
14429                                 (window.URL && URL.revokeObjectURL && URL) || 
14430                                 (window.webkitURL && webkitURL);
14431                         
14432          
14433          
14434          
14435         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14436         
14437         this.selectorEl.on('change', this.onFileSelected, this);
14438         if (this.images) {
14439             var t = this;
14440             this.images.forEach(function(img) {
14441                 t.addCard(img)
14442             });
14443             this.images = false;
14444         }
14445         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14446          
14447        
14448     },
14449     
14450    
14451     onClick : function(e)
14452     {
14453         e.preventDefault();
14454          
14455         this.selectorEl.dom.click();
14456          
14457     },
14458     
14459     onFileSelected : function(e)
14460     {
14461         e.preventDefault();
14462         
14463         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14464             return;
14465         }
14466         
14467         Roo.each(this.selectorEl.dom.files, function(file){    
14468             this.addFile(file);
14469         }, this);
14470          
14471     },
14472     
14473       
14474     
14475       
14476     
14477     addFile : function(file)
14478     {
14479            
14480         if(typeof(file) === 'string'){
14481             throw "Add file by name?"; // should not happen
14482             return;
14483         }
14484         
14485         if(!file || !this.urlAPI){
14486             return;
14487         }
14488         
14489         // file;
14490         // file.type;
14491         
14492         var _this = this;
14493         
14494         
14495         var url = _this.urlAPI.createObjectURL( file);
14496            
14497         this.addCard({
14498             id : Roo.bootstrap.form.CardUploader.ID--,
14499             is_uploaded : false,
14500             src : url,
14501             srcfile : file,
14502             title : file.name,
14503             mimetype : file.type,
14504             preview : false,
14505             is_deleted : 0
14506         });
14507         
14508     },
14509     
14510     /**
14511      * addCard - add an Attachment to the uploader
14512      * @param data - the data about the image to upload
14513      *
14514      * {
14515           id : 123
14516           title : "Title of file",
14517           is_uploaded : false,
14518           src : "http://.....",
14519           srcfile : { the File upload object },
14520           mimetype : file.type,
14521           preview : false,
14522           is_deleted : 0
14523           .. any other data...
14524         }
14525      *
14526      * 
14527     */
14528     
14529     addCard : function (data)
14530     {
14531         // hidden input element?
14532         // if the file is not an image...
14533         //then we need to use something other that and header_image
14534         var t = this;
14535         //   remove.....
14536         var footer = [
14537             {
14538                 xns : Roo.bootstrap,
14539                 xtype : 'CardFooter',
14540                  items: [
14541                     {
14542                         xns : Roo.bootstrap,
14543                         xtype : 'Element',
14544                         cls : 'd-flex',
14545                         items : [
14546                             
14547                             {
14548                                 xns : Roo.bootstrap,
14549                                 xtype : 'Button',
14550                                 html : String.format("<small>{0}</small>", data.title),
14551                                 cls : 'col-10 text-left',
14552                                 size: 'sm',
14553                                 weight: 'link',
14554                                 fa : 'download',
14555                                 listeners : {
14556                                     click : function() {
14557                                      
14558                                         t.fireEvent( "download", t, data );
14559                                     }
14560                                 }
14561                             },
14562                           
14563                             {
14564                                 xns : Roo.bootstrap,
14565                                 xtype : 'Button',
14566                                 style: 'max-height: 28px; ',
14567                                 size : 'sm',
14568                                 weight: 'danger',
14569                                 cls : 'col-2',
14570                                 fa : 'times',
14571                                 listeners : {
14572                                     click : function() {
14573                                         t.removeCard(data.id)
14574                                     }
14575                                 }
14576                             }
14577                         ]
14578                     }
14579                     
14580                 ] 
14581             }
14582             
14583         ];
14584         
14585         var cn = this.addxtype(
14586             {
14587                  
14588                 xns : Roo.bootstrap,
14589                 xtype : 'Card',
14590                 closeable : true,
14591                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14592                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14593                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14594                 data : data,
14595                 html : false,
14596                  
14597                 items : footer,
14598                 initEvents : function() {
14599                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14600                     var card = this;
14601                     this.imgEl = this.el.select('.card-img-top').first();
14602                     if (this.imgEl) {
14603                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14604                         this.imgEl.set({ 'pointer' : 'cursor' });
14605                                   
14606                     }
14607                     this.getCardFooter().addClass('p-1');
14608                     
14609                   
14610                 }
14611                 
14612             }
14613         );
14614         // dont' really need ot update items.
14615         // this.items.push(cn);
14616         this.fileCollection.add(cn);
14617         
14618         if (!data.srcfile) {
14619             this.updateInput();
14620             return;
14621         }
14622             
14623         var _t = this;
14624         var reader = new FileReader();
14625         reader.addEventListener("load", function() {  
14626             data.srcdata =  reader.result;
14627             _t.updateInput();
14628         });
14629         reader.readAsDataURL(data.srcfile);
14630         
14631         
14632         
14633     },
14634     removeCard : function(id)
14635     {
14636         
14637         var card  = this.fileCollection.get(id);
14638         card.data.is_deleted = 1;
14639         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14640         //this.fileCollection.remove(card);
14641         //this.items = this.items.filter(function(e) { return e != card });
14642         // dont' really need ot update items.
14643         card.el.dom.parentNode.removeChild(card.el.dom);
14644         this.updateInput();
14645
14646         
14647     },
14648     reset: function()
14649     {
14650         this.fileCollection.each(function(card) {
14651             if (card.el.dom && card.el.dom.parentNode) {
14652                 card.el.dom.parentNode.removeChild(card.el.dom);
14653             }
14654         });
14655         this.fileCollection.clear();
14656         this.updateInput();
14657     },
14658     
14659     updateInput : function()
14660     {
14661          var data = [];
14662         this.fileCollection.each(function(e) {
14663             data.push(e.data);
14664             
14665         });
14666         this.inputEl().dom.value = JSON.stringify(data);
14667         
14668         
14669         
14670     }
14671     
14672     
14673 });
14674
14675
14676 Roo.bootstrap.form.CardUploader.ID = -1;/*
14677  * Based on:
14678  * Ext JS Library 1.1.1
14679  * Copyright(c) 2006-2007, Ext JS, LLC.
14680  *
14681  * Originally Released Under LGPL - original licence link has changed is not relivant.
14682  *
14683  * Fork - LGPL
14684  * <script type="text/javascript">
14685  */
14686
14687
14688 /**
14689  * @class Roo.data.SortTypes
14690  * @singleton
14691  * Defines the default sorting (casting?) comparison functions used when sorting data.
14692  */
14693 Roo.data.SortTypes = {
14694     /**
14695      * Default sort that does nothing
14696      * @param {Mixed} s The value being converted
14697      * @return {Mixed} The comparison value
14698      */
14699     none : function(s){
14700         return s;
14701     },
14702     
14703     /**
14704      * The regular expression used to strip tags
14705      * @type {RegExp}
14706      * @property
14707      */
14708     stripTagsRE : /<\/?[^>]+>/gi,
14709     
14710     /**
14711      * Strips all HTML tags to sort on text only
14712      * @param {Mixed} s The value being converted
14713      * @return {String} The comparison value
14714      */
14715     asText : function(s){
14716         return String(s).replace(this.stripTagsRE, "");
14717     },
14718     
14719     /**
14720      * Strips all HTML tags to sort on text only - Case insensitive
14721      * @param {Mixed} s The value being converted
14722      * @return {String} The comparison value
14723      */
14724     asUCText : function(s){
14725         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14726     },
14727     
14728     /**
14729      * Case insensitive string
14730      * @param {Mixed} s The value being converted
14731      * @return {String} The comparison value
14732      */
14733     asUCString : function(s) {
14734         return String(s).toUpperCase();
14735     },
14736     
14737     /**
14738      * Date sorting
14739      * @param {Mixed} s The value being converted
14740      * @return {Number} The comparison value
14741      */
14742     asDate : function(s) {
14743         if(!s){
14744             return 0;
14745         }
14746         if(s instanceof Date){
14747             return s.getTime();
14748         }
14749         return Date.parse(String(s));
14750     },
14751     
14752     /**
14753      * Float sorting
14754      * @param {Mixed} s The value being converted
14755      * @return {Float} The comparison value
14756      */
14757     asFloat : function(s) {
14758         var val = parseFloat(String(s).replace(/,/g, ""));
14759         if(isNaN(val)) {
14760             val = 0;
14761         }
14762         return val;
14763     },
14764     
14765     /**
14766      * Integer sorting
14767      * @param {Mixed} s The value being converted
14768      * @return {Number} The comparison value
14769      */
14770     asInt : function(s) {
14771         var val = parseInt(String(s).replace(/,/g, ""));
14772         if(isNaN(val)) {
14773             val = 0;
14774         }
14775         return val;
14776     }
14777 };/*
14778  * Based on:
14779  * Ext JS Library 1.1.1
14780  * Copyright(c) 2006-2007, Ext JS, LLC.
14781  *
14782  * Originally Released Under LGPL - original licence link has changed is not relivant.
14783  *
14784  * Fork - LGPL
14785  * <script type="text/javascript">
14786  */
14787
14788 /**
14789 * @class Roo.data.Record
14790  * Instances of this class encapsulate both record <em>definition</em> information, and record
14791  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14792  * to access Records cached in an {@link Roo.data.Store} object.<br>
14793  * <p>
14794  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14795  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14796  * objects.<br>
14797  * <p>
14798  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14799  * @constructor
14800  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14801  * {@link #create}. The parameters are the same.
14802  * @param {Array} data An associative Array of data values keyed by the field name.
14803  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14804  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14805  * not specified an integer id is generated.
14806  */
14807 Roo.data.Record = function(data, id){
14808     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14809     this.data = data;
14810 };
14811
14812 /**
14813  * Generate a constructor for a specific record layout.
14814  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14815  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14816  * Each field definition object may contain the following properties: <ul>
14817  * <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,
14818  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14819  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14820  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14821  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14822  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14823  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14824  * this may be omitted.</p></li>
14825  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14826  * <ul><li>auto (Default, implies no conversion)</li>
14827  * <li>string</li>
14828  * <li>int</li>
14829  * <li>float</li>
14830  * <li>boolean</li>
14831  * <li>date</li></ul></p></li>
14832  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14833  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14834  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14835  * by the Reader into an object that will be stored in the Record. It is passed the
14836  * following parameters:<ul>
14837  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14838  * </ul></p></li>
14839  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14840  * </ul>
14841  * <br>usage:<br><pre><code>
14842 var TopicRecord = Roo.data.Record.create(
14843     {name: 'title', mapping: 'topic_title'},
14844     {name: 'author', mapping: 'username'},
14845     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14846     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14847     {name: 'lastPoster', mapping: 'user2'},
14848     {name: 'excerpt', mapping: 'post_text'}
14849 );
14850
14851 var myNewRecord = new TopicRecord({
14852     title: 'Do my job please',
14853     author: 'noobie',
14854     totalPosts: 1,
14855     lastPost: new Date(),
14856     lastPoster: 'Animal',
14857     excerpt: 'No way dude!'
14858 });
14859 myStore.add(myNewRecord);
14860 </code></pre>
14861  * @method create
14862  * @static
14863  */
14864 Roo.data.Record.create = function(o){
14865     var f = function(){
14866         f.superclass.constructor.apply(this, arguments);
14867     };
14868     Roo.extend(f, Roo.data.Record);
14869     var p = f.prototype;
14870     p.fields = new Roo.util.MixedCollection(false, function(field){
14871         return field.name;
14872     });
14873     for(var i = 0, len = o.length; i < len; i++){
14874         p.fields.add(new Roo.data.Field(o[i]));
14875     }
14876     f.getField = function(name){
14877         return p.fields.get(name);  
14878     };
14879     return f;
14880 };
14881
14882 Roo.data.Record.AUTO_ID = 1000;
14883 Roo.data.Record.EDIT = 'edit';
14884 Roo.data.Record.REJECT = 'reject';
14885 Roo.data.Record.COMMIT = 'commit';
14886
14887 Roo.data.Record.prototype = {
14888     /**
14889      * Readonly flag - true if this record has been modified.
14890      * @type Boolean
14891      */
14892     dirty : false,
14893     editing : false,
14894     error: null,
14895     modified: null,
14896
14897     // private
14898     join : function(store){
14899         this.store = store;
14900     },
14901
14902     /**
14903      * Set the named field to the specified value.
14904      * @param {String} name The name of the field to set.
14905      * @param {Object} value The value to set the field to.
14906      */
14907     set : function(name, value){
14908         if(this.data[name] == value){
14909             return;
14910         }
14911         this.dirty = true;
14912         if(!this.modified){
14913             this.modified = {};
14914         }
14915         if(typeof this.modified[name] == 'undefined'){
14916             this.modified[name] = this.data[name];
14917         }
14918         this.data[name] = value;
14919         if(!this.editing && this.store){
14920             this.store.afterEdit(this);
14921         }       
14922     },
14923
14924     /**
14925      * Get the value of the named field.
14926      * @param {String} name The name of the field to get the value of.
14927      * @return {Object} The value of the field.
14928      */
14929     get : function(name){
14930         return this.data[name]; 
14931     },
14932
14933     // private
14934     beginEdit : function(){
14935         this.editing = true;
14936         this.modified = {}; 
14937     },
14938
14939     // private
14940     cancelEdit : function(){
14941         this.editing = false;
14942         delete this.modified;
14943     },
14944
14945     // private
14946     endEdit : function(){
14947         this.editing = false;
14948         if(this.dirty && this.store){
14949             this.store.afterEdit(this);
14950         }
14951     },
14952
14953     /**
14954      * Usually called by the {@link Roo.data.Store} which owns the Record.
14955      * Rejects all changes made to the Record since either creation, or the last commit operation.
14956      * Modified fields are reverted to their original values.
14957      * <p>
14958      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14959      * of reject operations.
14960      */
14961     reject : function(){
14962         var m = this.modified;
14963         for(var n in m){
14964             if(typeof m[n] != "function"){
14965                 this.data[n] = m[n];
14966             }
14967         }
14968         this.dirty = false;
14969         delete this.modified;
14970         this.editing = false;
14971         if(this.store){
14972             this.store.afterReject(this);
14973         }
14974     },
14975
14976     /**
14977      * Usually called by the {@link Roo.data.Store} which owns the Record.
14978      * Commits all changes made to the Record since either creation, or the last commit operation.
14979      * <p>
14980      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14981      * of commit operations.
14982      */
14983     commit : function(){
14984         this.dirty = false;
14985         delete this.modified;
14986         this.editing = false;
14987         if(this.store){
14988             this.store.afterCommit(this);
14989         }
14990     },
14991
14992     // private
14993     hasError : function(){
14994         return this.error != null;
14995     },
14996
14997     // private
14998     clearError : function(){
14999         this.error = null;
15000     },
15001
15002     /**
15003      * Creates a copy of this record.
15004      * @param {String} id (optional) A new record id if you don't want to use this record's id
15005      * @return {Record}
15006      */
15007     copy : function(newId) {
15008         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15009     }
15010 };/*
15011  * Based on:
15012  * Ext JS Library 1.1.1
15013  * Copyright(c) 2006-2007, Ext JS, LLC.
15014  *
15015  * Originally Released Under LGPL - original licence link has changed is not relivant.
15016  *
15017  * Fork - LGPL
15018  * <script type="text/javascript">
15019  */
15020
15021
15022
15023 /**
15024  * @class Roo.data.Store
15025  * @extends Roo.util.Observable
15026  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15027  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15028  * <p>
15029  * 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
15030  * has no knowledge of the format of the data returned by the Proxy.<br>
15031  * <p>
15032  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15033  * instances from the data object. These records are cached and made available through accessor functions.
15034  * @constructor
15035  * Creates a new Store.
15036  * @param {Object} config A config object containing the objects needed for the Store to access data,
15037  * and read the data into Records.
15038  */
15039 Roo.data.Store = function(config){
15040     this.data = new Roo.util.MixedCollection(false);
15041     this.data.getKey = function(o){
15042         return o.id;
15043     };
15044     this.baseParams = {};
15045     // private
15046     this.paramNames = {
15047         "start" : "start",
15048         "limit" : "limit",
15049         "sort" : "sort",
15050         "dir" : "dir",
15051         "multisort" : "_multisort"
15052     };
15053
15054     if(config && config.data){
15055         this.inlineData = config.data;
15056         delete config.data;
15057     }
15058
15059     Roo.apply(this, config);
15060     
15061     if(this.reader){ // reader passed
15062         this.reader = Roo.factory(this.reader, Roo.data);
15063         this.reader.xmodule = this.xmodule || false;
15064         if(!this.recordType){
15065             this.recordType = this.reader.recordType;
15066         }
15067         if(this.reader.onMetaChange){
15068             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15069         }
15070     }
15071
15072     if(this.recordType){
15073         this.fields = this.recordType.prototype.fields;
15074     }
15075     this.modified = [];
15076
15077     this.addEvents({
15078         /**
15079          * @event datachanged
15080          * Fires when the data cache has changed, and a widget which is using this Store
15081          * as a Record cache should refresh its view.
15082          * @param {Store} this
15083          */
15084         datachanged : true,
15085         /**
15086          * @event metachange
15087          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15088          * @param {Store} this
15089          * @param {Object} meta The JSON metadata
15090          */
15091         metachange : true,
15092         /**
15093          * @event add
15094          * Fires when Records have been added to the Store
15095          * @param {Store} this
15096          * @param {Roo.data.Record[]} records The array of Records added
15097          * @param {Number} index The index at which the record(s) were added
15098          */
15099         add : true,
15100         /**
15101          * @event remove
15102          * Fires when a Record has been removed from the Store
15103          * @param {Store} this
15104          * @param {Roo.data.Record} record The Record that was removed
15105          * @param {Number} index The index at which the record was removed
15106          */
15107         remove : true,
15108         /**
15109          * @event update
15110          * Fires when a Record has been updated
15111          * @param {Store} this
15112          * @param {Roo.data.Record} record The Record that was updated
15113          * @param {String} operation The update operation being performed.  Value may be one of:
15114          * <pre><code>
15115  Roo.data.Record.EDIT
15116  Roo.data.Record.REJECT
15117  Roo.data.Record.COMMIT
15118          * </code></pre>
15119          */
15120         update : true,
15121         /**
15122          * @event clear
15123          * Fires when the data cache has been cleared.
15124          * @param {Store} this
15125          */
15126         clear : true,
15127         /**
15128          * @event beforeload
15129          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15130          * the load action will be canceled.
15131          * @param {Store} this
15132          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15133          */
15134         beforeload : true,
15135         /**
15136          * @event beforeloadadd
15137          * Fires after a new set of Records has been loaded.
15138          * @param {Store} this
15139          * @param {Roo.data.Record[]} records The Records that were loaded
15140          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15141          */
15142         beforeloadadd : true,
15143         /**
15144          * @event load
15145          * Fires after a new set of Records has been loaded, before they are added to the store.
15146          * @param {Store} this
15147          * @param {Roo.data.Record[]} records The Records that were loaded
15148          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15149          * @params {Object} return from reader
15150          */
15151         load : true,
15152         /**
15153          * @event loadexception
15154          * Fires if an exception occurs in the Proxy during loading.
15155          * Called with the signature of the Proxy's "loadexception" event.
15156          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15157          * 
15158          * @param {Proxy} 
15159          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15160          * @param {Object} load options 
15161          * @param {Object} jsonData from your request (normally this contains the Exception)
15162          */
15163         loadexception : true
15164     });
15165     
15166     if(this.proxy){
15167         this.proxy = Roo.factory(this.proxy, Roo.data);
15168         this.proxy.xmodule = this.xmodule || false;
15169         this.relayEvents(this.proxy,  ["loadexception"]);
15170     }
15171     this.sortToggle = {};
15172     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15173
15174     Roo.data.Store.superclass.constructor.call(this);
15175
15176     if(this.inlineData){
15177         this.loadData(this.inlineData);
15178         delete this.inlineData;
15179     }
15180 };
15181
15182 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15183      /**
15184     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15185     * without a remote query - used by combo/forms at present.
15186     */
15187     
15188     /**
15189     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15190     */
15191     /**
15192     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15193     */
15194     /**
15195     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15196     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15197     */
15198     /**
15199     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15200     * on any HTTP request
15201     */
15202     /**
15203     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15204     */
15205     /**
15206     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15207     */
15208     multiSort: false,
15209     /**
15210     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15211     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15212     */
15213     remoteSort : false,
15214
15215     /**
15216     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15217      * loaded or when a record is removed. (defaults to false).
15218     */
15219     pruneModifiedRecords : false,
15220
15221     // private
15222     lastOptions : null,
15223
15224     /**
15225      * Add Records to the Store and fires the add event.
15226      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15227      */
15228     add : function(records){
15229         records = [].concat(records);
15230         for(var i = 0, len = records.length; i < len; i++){
15231             records[i].join(this);
15232         }
15233         var index = this.data.length;
15234         this.data.addAll(records);
15235         this.fireEvent("add", this, records, index);
15236     },
15237
15238     /**
15239      * Remove a Record from the Store and fires the remove event.
15240      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15241      */
15242     remove : function(record){
15243         var index = this.data.indexOf(record);
15244         this.data.removeAt(index);
15245  
15246         if(this.pruneModifiedRecords){
15247             this.modified.remove(record);
15248         }
15249         this.fireEvent("remove", this, record, index);
15250     },
15251
15252     /**
15253      * Remove all Records from the Store and fires the clear event.
15254      */
15255     removeAll : function(){
15256         this.data.clear();
15257         if(this.pruneModifiedRecords){
15258             this.modified = [];
15259         }
15260         this.fireEvent("clear", this);
15261     },
15262
15263     /**
15264      * Inserts Records to the Store at the given index and fires the add event.
15265      * @param {Number} index The start index at which to insert the passed Records.
15266      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15267      */
15268     insert : function(index, records){
15269         records = [].concat(records);
15270         for(var i = 0, len = records.length; i < len; i++){
15271             this.data.insert(index, records[i]);
15272             records[i].join(this);
15273         }
15274         this.fireEvent("add", this, records, index);
15275     },
15276
15277     /**
15278      * Get the index within the cache of the passed Record.
15279      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15280      * @return {Number} The index of the passed Record. Returns -1 if not found.
15281      */
15282     indexOf : function(record){
15283         return this.data.indexOf(record);
15284     },
15285
15286     /**
15287      * Get the index within the cache of the Record with the passed id.
15288      * @param {String} id The id of the Record to find.
15289      * @return {Number} The index of the Record. Returns -1 if not found.
15290      */
15291     indexOfId : function(id){
15292         return this.data.indexOfKey(id);
15293     },
15294
15295     /**
15296      * Get the Record with the specified id.
15297      * @param {String} id The id of the Record to find.
15298      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15299      */
15300     getById : function(id){
15301         return this.data.key(id);
15302     },
15303
15304     /**
15305      * Get the Record at the specified index.
15306      * @param {Number} index The index of the Record to find.
15307      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15308      */
15309     getAt : function(index){
15310         return this.data.itemAt(index);
15311     },
15312
15313     /**
15314      * Returns a range of Records between specified indices.
15315      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15316      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15317      * @return {Roo.data.Record[]} An array of Records
15318      */
15319     getRange : function(start, end){
15320         return this.data.getRange(start, end);
15321     },
15322
15323     // private
15324     storeOptions : function(o){
15325         o = Roo.apply({}, o);
15326         delete o.callback;
15327         delete o.scope;
15328         this.lastOptions = o;
15329     },
15330
15331     /**
15332      * Loads the Record cache from the configured Proxy using the configured Reader.
15333      * <p>
15334      * If using remote paging, then the first load call must specify the <em>start</em>
15335      * and <em>limit</em> properties in the options.params property to establish the initial
15336      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15337      * <p>
15338      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15339      * and this call will return before the new data has been loaded. Perform any post-processing
15340      * in a callback function, or in a "load" event handler.</strong>
15341      * <p>
15342      * @param {Object} options An object containing properties which control loading options:<ul>
15343      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15344      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15345      * passed the following arguments:<ul>
15346      * <li>r : Roo.data.Record[]</li>
15347      * <li>options: Options object from the load call</li>
15348      * <li>success: Boolean success indicator</li></ul></li>
15349      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15350      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15351      * </ul>
15352      */
15353     load : function(options){
15354         options = options || {};
15355         if(this.fireEvent("beforeload", this, options) !== false){
15356             this.storeOptions(options);
15357             var p = Roo.apply(options.params || {}, this.baseParams);
15358             // if meta was not loaded from remote source.. try requesting it.
15359             if (!this.reader.metaFromRemote) {
15360                 p._requestMeta = 1;
15361             }
15362             if(this.sortInfo && this.remoteSort){
15363                 var pn = this.paramNames;
15364                 p[pn["sort"]] = this.sortInfo.field;
15365                 p[pn["dir"]] = this.sortInfo.direction;
15366             }
15367             if (this.multiSort) {
15368                 var pn = this.paramNames;
15369                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15370             }
15371             
15372             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15373         }
15374     },
15375
15376     /**
15377      * Reloads the Record cache from the configured Proxy using the configured Reader and
15378      * the options from the last load operation performed.
15379      * @param {Object} options (optional) An object containing properties which may override the options
15380      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15381      * the most recently used options are reused).
15382      */
15383     reload : function(options){
15384         this.load(Roo.applyIf(options||{}, this.lastOptions));
15385     },
15386
15387     // private
15388     // Called as a callback by the Reader during a load operation.
15389     loadRecords : function(o, options, success){
15390         if(!o || success === false){
15391             if(success !== false){
15392                 this.fireEvent("load", this, [], options, o);
15393             }
15394             if(options.callback){
15395                 options.callback.call(options.scope || this, [], options, false);
15396             }
15397             return;
15398         }
15399         // if data returned failure - throw an exception.
15400         if (o.success === false) {
15401             // show a message if no listener is registered.
15402             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15403                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15404             }
15405             // loadmask wil be hooked into this..
15406             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15407             return;
15408         }
15409         var r = o.records, t = o.totalRecords || r.length;
15410         
15411         this.fireEvent("beforeloadadd", this, r, options, o);
15412         
15413         if(!options || options.add !== true){
15414             if(this.pruneModifiedRecords){
15415                 this.modified = [];
15416             }
15417             for(var i = 0, len = r.length; i < len; i++){
15418                 r[i].join(this);
15419             }
15420             if(this.snapshot){
15421                 this.data = this.snapshot;
15422                 delete this.snapshot;
15423             }
15424             this.data.clear();
15425             this.data.addAll(r);
15426             this.totalLength = t;
15427             this.applySort();
15428             this.fireEvent("datachanged", this);
15429         }else{
15430             this.totalLength = Math.max(t, this.data.length+r.length);
15431             this.add(r);
15432         }
15433         
15434         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15435                 
15436             var e = new Roo.data.Record({});
15437
15438             e.set(this.parent.displayField, this.parent.emptyTitle);
15439             e.set(this.parent.valueField, '');
15440
15441             this.insert(0, e);
15442         }
15443             
15444         this.fireEvent("load", this, r, options, o);
15445         if(options.callback){
15446             options.callback.call(options.scope || this, r, options, true);
15447         }
15448     },
15449
15450
15451     /**
15452      * Loads data from a passed data block. A Reader which understands the format of the data
15453      * must have been configured in the constructor.
15454      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15455      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15456      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15457      */
15458     loadData : function(o, append){
15459         var r = this.reader.readRecords(o);
15460         this.loadRecords(r, {add: append}, true);
15461     },
15462     
15463      /**
15464      * using 'cn' the nested child reader read the child array into it's child stores.
15465      * @param {Object} rec The record with a 'children array
15466      */
15467     loadDataFromChildren : function(rec)
15468     {
15469         this.loadData(this.reader.toLoadData(rec));
15470     },
15471     
15472
15473     /**
15474      * Gets the number of cached records.
15475      * <p>
15476      * <em>If using paging, this may not be the total size of the dataset. If the data object
15477      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15478      * the data set size</em>
15479      */
15480     getCount : function(){
15481         return this.data.length || 0;
15482     },
15483
15484     /**
15485      * Gets the total number of records in the dataset as returned by the server.
15486      * <p>
15487      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15488      * the dataset size</em>
15489      */
15490     getTotalCount : function(){
15491         return this.totalLength || 0;
15492     },
15493
15494     /**
15495      * Returns the sort state of the Store as an object with two properties:
15496      * <pre><code>
15497  field {String} The name of the field by which the Records are sorted
15498  direction {String} The sort order, "ASC" or "DESC"
15499      * </code></pre>
15500      */
15501     getSortState : function(){
15502         return this.sortInfo;
15503     },
15504
15505     // private
15506     applySort : function(){
15507         if(this.sortInfo && !this.remoteSort){
15508             var s = this.sortInfo, f = s.field;
15509             var st = this.fields.get(f).sortType;
15510             var fn = function(r1, r2){
15511                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15512                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15513             };
15514             this.data.sort(s.direction, fn);
15515             if(this.snapshot && this.snapshot != this.data){
15516                 this.snapshot.sort(s.direction, fn);
15517             }
15518         }
15519     },
15520
15521     /**
15522      * Sets the default sort column and order to be used by the next load operation.
15523      * @param {String} fieldName The name of the field to sort by.
15524      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15525      */
15526     setDefaultSort : function(field, dir){
15527         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15528     },
15529
15530     /**
15531      * Sort the Records.
15532      * If remote sorting is used, the sort is performed on the server, and the cache is
15533      * reloaded. If local sorting is used, the cache is sorted internally.
15534      * @param {String} fieldName The name of the field to sort by.
15535      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15536      */
15537     sort : function(fieldName, dir){
15538         var f = this.fields.get(fieldName);
15539         if(!dir){
15540             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15541             
15542             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15543                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15544             }else{
15545                 dir = f.sortDir;
15546             }
15547         }
15548         this.sortToggle[f.name] = dir;
15549         this.sortInfo = {field: f.name, direction: dir};
15550         if(!this.remoteSort){
15551             this.applySort();
15552             this.fireEvent("datachanged", this);
15553         }else{
15554             this.load(this.lastOptions);
15555         }
15556     },
15557
15558     /**
15559      * Calls the specified function for each of the Records in the cache.
15560      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15561      * Returning <em>false</em> aborts and exits the iteration.
15562      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15563      */
15564     each : function(fn, scope){
15565         this.data.each(fn, scope);
15566     },
15567
15568     /**
15569      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15570      * (e.g., during paging).
15571      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15572      */
15573     getModifiedRecords : function(){
15574         return this.modified;
15575     },
15576
15577     // private
15578     createFilterFn : function(property, value, anyMatch){
15579         if(!value.exec){ // not a regex
15580             value = String(value);
15581             if(value.length == 0){
15582                 return false;
15583             }
15584             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15585         }
15586         return function(r){
15587             return value.test(r.data[property]);
15588         };
15589     },
15590
15591     /**
15592      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15593      * @param {String} property A field on your records
15594      * @param {Number} start The record index to start at (defaults to 0)
15595      * @param {Number} end The last record index to include (defaults to length - 1)
15596      * @return {Number} The sum
15597      */
15598     sum : function(property, start, end){
15599         var rs = this.data.items, v = 0;
15600         start = start || 0;
15601         end = (end || end === 0) ? end : rs.length-1;
15602
15603         for(var i = start; i <= end; i++){
15604             v += (rs[i].data[property] || 0);
15605         }
15606         return v;
15607     },
15608
15609     /**
15610      * Filter the records by a specified property.
15611      * @param {String} field A field on your records
15612      * @param {String/RegExp} value Either a string that the field
15613      * should start with or a RegExp to test against the field
15614      * @param {Boolean} anyMatch True to match any part not just the beginning
15615      */
15616     filter : function(property, value, anyMatch){
15617         var fn = this.createFilterFn(property, value, anyMatch);
15618         return fn ? this.filterBy(fn) : this.clearFilter();
15619     },
15620
15621     /**
15622      * Filter by a function. The specified function will be called with each
15623      * record in this data source. If the function returns true the record is included,
15624      * otherwise it is filtered.
15625      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15626      * @param {Object} scope (optional) The scope of the function (defaults to this)
15627      */
15628     filterBy : function(fn, scope){
15629         this.snapshot = this.snapshot || this.data;
15630         this.data = this.queryBy(fn, scope||this);
15631         this.fireEvent("datachanged", this);
15632     },
15633
15634     /**
15635      * Query the records by a specified property.
15636      * @param {String} field A field on your records
15637      * @param {String/RegExp} value Either a string that the field
15638      * should start with or a RegExp to test against the field
15639      * @param {Boolean} anyMatch True to match any part not just the beginning
15640      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15641      */
15642     query : function(property, value, anyMatch){
15643         var fn = this.createFilterFn(property, value, anyMatch);
15644         return fn ? this.queryBy(fn) : this.data.clone();
15645     },
15646
15647     /**
15648      * Query by a function. The specified function will be called with each
15649      * record in this data source. If the function returns true the record is included
15650      * in the results.
15651      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15652      * @param {Object} scope (optional) The scope of the function (defaults to this)
15653       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15654      **/
15655     queryBy : function(fn, scope){
15656         var data = this.snapshot || this.data;
15657         return data.filterBy(fn, scope||this);
15658     },
15659
15660     /**
15661      * Collects unique values for a particular dataIndex from this store.
15662      * @param {String} dataIndex The property to collect
15663      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15664      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15665      * @return {Array} An array of the unique values
15666      **/
15667     collect : function(dataIndex, allowNull, bypassFilter){
15668         var d = (bypassFilter === true && this.snapshot) ?
15669                 this.snapshot.items : this.data.items;
15670         var v, sv, r = [], l = {};
15671         for(var i = 0, len = d.length; i < len; i++){
15672             v = d[i].data[dataIndex];
15673             sv = String(v);
15674             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15675                 l[sv] = true;
15676                 r[r.length] = v;
15677             }
15678         }
15679         return r;
15680     },
15681
15682     /**
15683      * Revert to a view of the Record cache with no filtering applied.
15684      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15685      */
15686     clearFilter : function(suppressEvent){
15687         if(this.snapshot && this.snapshot != this.data){
15688             this.data = this.snapshot;
15689             delete this.snapshot;
15690             if(suppressEvent !== true){
15691                 this.fireEvent("datachanged", this);
15692             }
15693         }
15694     },
15695
15696     // private
15697     afterEdit : function(record){
15698         if(this.modified.indexOf(record) == -1){
15699             this.modified.push(record);
15700         }
15701         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15702     },
15703     
15704     // private
15705     afterReject : function(record){
15706         this.modified.remove(record);
15707         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15708     },
15709
15710     // private
15711     afterCommit : function(record){
15712         this.modified.remove(record);
15713         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15714     },
15715
15716     /**
15717      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15718      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15719      */
15720     commitChanges : function(){
15721         var m = this.modified.slice(0);
15722         this.modified = [];
15723         for(var i = 0, len = m.length; i < len; i++){
15724             m[i].commit();
15725         }
15726     },
15727
15728     /**
15729      * Cancel outstanding changes on all changed records.
15730      */
15731     rejectChanges : function(){
15732         var m = this.modified.slice(0);
15733         this.modified = [];
15734         for(var i = 0, len = m.length; i < len; i++){
15735             m[i].reject();
15736         }
15737     },
15738
15739     onMetaChange : function(meta, rtype, o){
15740         this.recordType = rtype;
15741         this.fields = rtype.prototype.fields;
15742         delete this.snapshot;
15743         this.sortInfo = meta.sortInfo || this.sortInfo;
15744         this.modified = [];
15745         this.fireEvent('metachange', this, this.reader.meta);
15746     },
15747     
15748     moveIndex : function(data, type)
15749     {
15750         var index = this.indexOf(data);
15751         
15752         var newIndex = index + type;
15753         
15754         this.remove(data);
15755         
15756         this.insert(newIndex, data);
15757         
15758     }
15759 });/*
15760  * Based on:
15761  * Ext JS Library 1.1.1
15762  * Copyright(c) 2006-2007, Ext JS, LLC.
15763  *
15764  * Originally Released Under LGPL - original licence link has changed is not relivant.
15765  *
15766  * Fork - LGPL
15767  * <script type="text/javascript">
15768  */
15769
15770 /**
15771  * @class Roo.data.SimpleStore
15772  * @extends Roo.data.Store
15773  * Small helper class to make creating Stores from Array data easier.
15774  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15775  * @cfg {Array} fields An array of field definition objects, or field name strings.
15776  * @cfg {Object} an existing reader (eg. copied from another store)
15777  * @cfg {Array} data The multi-dimensional array of data
15778  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15779  * @cfg {Roo.data.Reader} reader  [not-required] 
15780  * @constructor
15781  * @param {Object} config
15782  */
15783 Roo.data.SimpleStore = function(config)
15784 {
15785     Roo.data.SimpleStore.superclass.constructor.call(this, {
15786         isLocal : true,
15787         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15788                 id: config.id
15789             },
15790             Roo.data.Record.create(config.fields)
15791         ),
15792         proxy : new Roo.data.MemoryProxy(config.data)
15793     });
15794     this.load();
15795 };
15796 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15797  * Based on:
15798  * Ext JS Library 1.1.1
15799  * Copyright(c) 2006-2007, Ext JS, LLC.
15800  *
15801  * Originally Released Under LGPL - original licence link has changed is not relivant.
15802  *
15803  * Fork - LGPL
15804  * <script type="text/javascript">
15805  */
15806
15807 /**
15808 /**
15809  * @extends Roo.data.Store
15810  * @class Roo.data.JsonStore
15811  * Small helper class to make creating Stores for JSON data easier. <br/>
15812 <pre><code>
15813 var store = new Roo.data.JsonStore({
15814     url: 'get-images.php',
15815     root: 'images',
15816     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15817 });
15818 </code></pre>
15819  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15820  * JsonReader and HttpProxy (unless inline data is provided).</b>
15821  * @cfg {Array} fields An array of field definition objects, or field name strings.
15822  * @constructor
15823  * @param {Object} config
15824  */
15825 Roo.data.JsonStore = function(c){
15826     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15827         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15828         reader: new Roo.data.JsonReader(c, c.fields)
15829     }));
15830 };
15831 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15832  * Based on:
15833  * Ext JS Library 1.1.1
15834  * Copyright(c) 2006-2007, Ext JS, LLC.
15835  *
15836  * Originally Released Under LGPL - original licence link has changed is not relivant.
15837  *
15838  * Fork - LGPL
15839  * <script type="text/javascript">
15840  */
15841
15842  
15843 Roo.data.Field = function(config){
15844     if(typeof config == "string"){
15845         config = {name: config};
15846     }
15847     Roo.apply(this, config);
15848     
15849     if(!this.type){
15850         this.type = "auto";
15851     }
15852     
15853     var st = Roo.data.SortTypes;
15854     // named sortTypes are supported, here we look them up
15855     if(typeof this.sortType == "string"){
15856         this.sortType = st[this.sortType];
15857     }
15858     
15859     // set default sortType for strings and dates
15860     if(!this.sortType){
15861         switch(this.type){
15862             case "string":
15863                 this.sortType = st.asUCString;
15864                 break;
15865             case "date":
15866                 this.sortType = st.asDate;
15867                 break;
15868             default:
15869                 this.sortType = st.none;
15870         }
15871     }
15872
15873     // define once
15874     var stripRe = /[\$,%]/g;
15875
15876     // prebuilt conversion function for this field, instead of
15877     // switching every time we're reading a value
15878     if(!this.convert){
15879         var cv, dateFormat = this.dateFormat;
15880         switch(this.type){
15881             case "":
15882             case "auto":
15883             case undefined:
15884                 cv = function(v){ return v; };
15885                 break;
15886             case "string":
15887                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15888                 break;
15889             case "int":
15890                 cv = function(v){
15891                     return v !== undefined && v !== null && v !== '' ?
15892                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15893                     };
15894                 break;
15895             case "float":
15896                 cv = function(v){
15897                     return v !== undefined && v !== null && v !== '' ?
15898                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15899                     };
15900                 break;
15901             case "bool":
15902             case "boolean":
15903                 cv = function(v){ return v === true || v === "true" || v == 1; };
15904                 break;
15905             case "date":
15906                 cv = function(v){
15907                     if(!v){
15908                         return '';
15909                     }
15910                     if(v instanceof Date){
15911                         return v;
15912                     }
15913                     if(dateFormat){
15914                         if(dateFormat == "timestamp"){
15915                             return new Date(v*1000);
15916                         }
15917                         return Date.parseDate(v, dateFormat);
15918                     }
15919                     var parsed = Date.parse(v);
15920                     return parsed ? new Date(parsed) : null;
15921                 };
15922              break;
15923             
15924         }
15925         this.convert = cv;
15926     }
15927 };
15928
15929 Roo.data.Field.prototype = {
15930     dateFormat: null,
15931     defaultValue: "",
15932     mapping: null,
15933     sortType : null,
15934     sortDir : "ASC"
15935 };/*
15936  * Based on:
15937  * Ext JS Library 1.1.1
15938  * Copyright(c) 2006-2007, Ext JS, LLC.
15939  *
15940  * Originally Released Under LGPL - original licence link has changed is not relivant.
15941  *
15942  * Fork - LGPL
15943  * <script type="text/javascript">
15944  */
15945  
15946 // Base class for reading structured data from a data source.  This class is intended to be
15947 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15948
15949 /**
15950  * @class Roo.data.DataReader
15951  * @abstract
15952  * Base class for reading structured data from a data source.  This class is intended to be
15953  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15954  */
15955
15956 Roo.data.DataReader = function(meta, recordType){
15957     
15958     this.meta = meta;
15959     
15960     this.recordType = recordType instanceof Array ? 
15961         Roo.data.Record.create(recordType) : recordType;
15962 };
15963
15964 Roo.data.DataReader.prototype = {
15965     
15966     
15967     readerType : 'Data',
15968      /**
15969      * Create an empty record
15970      * @param {Object} data (optional) - overlay some values
15971      * @return {Roo.data.Record} record created.
15972      */
15973     newRow :  function(d) {
15974         var da =  {};
15975         this.recordType.prototype.fields.each(function(c) {
15976             switch( c.type) {
15977                 case 'int' : da[c.name] = 0; break;
15978                 case 'date' : da[c.name] = new Date(); break;
15979                 case 'float' : da[c.name] = 0.0; break;
15980                 case 'boolean' : da[c.name] = false; break;
15981                 default : da[c.name] = ""; break;
15982             }
15983             
15984         });
15985         return new this.recordType(Roo.apply(da, d));
15986     }
15987     
15988     
15989 };/*
15990  * Based on:
15991  * Ext JS Library 1.1.1
15992  * Copyright(c) 2006-2007, Ext JS, LLC.
15993  *
15994  * Originally Released Under LGPL - original licence link has changed is not relivant.
15995  *
15996  * Fork - LGPL
15997  * <script type="text/javascript">
15998  */
15999
16000 /**
16001  * @class Roo.data.DataProxy
16002  * @extends Roo.util.Observable
16003  * @abstract
16004  * This class is an abstract base class for implementations which provide retrieval of
16005  * unformatted data objects.<br>
16006  * <p>
16007  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16008  * (of the appropriate type which knows how to parse the data object) to provide a block of
16009  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16010  * <p>
16011  * Custom implementations must implement the load method as described in
16012  * {@link Roo.data.HttpProxy#load}.
16013  */
16014 Roo.data.DataProxy = function(){
16015     this.addEvents({
16016         /**
16017          * @event beforeload
16018          * Fires before a network request is made to retrieve a data object.
16019          * @param {Object} This DataProxy object.
16020          * @param {Object} params The params parameter to the load function.
16021          */
16022         beforeload : true,
16023         /**
16024          * @event load
16025          * Fires before the load method's callback is called.
16026          * @param {Object} This DataProxy object.
16027          * @param {Object} o The data object.
16028          * @param {Object} arg The callback argument object passed to the load function.
16029          */
16030         load : true,
16031         /**
16032          * @event loadexception
16033          * Fires if an Exception occurs during data retrieval.
16034          * @param {Object} This DataProxy object.
16035          * @param {Object} o The data object.
16036          * @param {Object} arg The callback argument object passed to the load function.
16037          * @param {Object} e The Exception.
16038          */
16039         loadexception : true
16040     });
16041     Roo.data.DataProxy.superclass.constructor.call(this);
16042 };
16043
16044 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16045
16046     /**
16047      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16048      */
16049 /*
16050  * Based on:
16051  * Ext JS Library 1.1.1
16052  * Copyright(c) 2006-2007, Ext JS, LLC.
16053  *
16054  * Originally Released Under LGPL - original licence link has changed is not relivant.
16055  *
16056  * Fork - LGPL
16057  * <script type="text/javascript">
16058  */
16059 /**
16060  * @class Roo.data.MemoryProxy
16061  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16062  * to the Reader when its load method is called.
16063  * @constructor
16064  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16065  */
16066 Roo.data.MemoryProxy = function(data){
16067     if (data.data) {
16068         data = data.data;
16069     }
16070     Roo.data.MemoryProxy.superclass.constructor.call(this);
16071     this.data = data;
16072 };
16073
16074 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16075     
16076     /**
16077      * Load data from the requested source (in this case an in-memory
16078      * data object passed to the constructor), read the data object into
16079      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16080      * process that block using the passed callback.
16081      * @param {Object} params This parameter is not used by the MemoryProxy class.
16082      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16083      * object into a block of Roo.data.Records.
16084      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16085      * The function must be passed <ul>
16086      * <li>The Record block object</li>
16087      * <li>The "arg" argument from the load function</li>
16088      * <li>A boolean success indicator</li>
16089      * </ul>
16090      * @param {Object} scope The scope in which to call the callback
16091      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16092      */
16093     load : function(params, reader, callback, scope, arg){
16094         params = params || {};
16095         var result;
16096         try {
16097             result = reader.readRecords(params.data ? params.data :this.data);
16098         }catch(e){
16099             this.fireEvent("loadexception", this, arg, null, e);
16100             callback.call(scope, null, arg, false);
16101             return;
16102         }
16103         callback.call(scope, result, arg, true);
16104     },
16105     
16106     // private
16107     update : function(params, records){
16108         
16109     }
16110 });/*
16111  * Based on:
16112  * Ext JS Library 1.1.1
16113  * Copyright(c) 2006-2007, Ext JS, LLC.
16114  *
16115  * Originally Released Under LGPL - original licence link has changed is not relivant.
16116  *
16117  * Fork - LGPL
16118  * <script type="text/javascript">
16119  */
16120 /**
16121  * @class Roo.data.HttpProxy
16122  * @extends Roo.data.DataProxy
16123  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16124  * configured to reference a certain URL.<br><br>
16125  * <p>
16126  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16127  * from which the running page was served.<br><br>
16128  * <p>
16129  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16130  * <p>
16131  * Be aware that to enable the browser to parse an XML document, the server must set
16132  * the Content-Type header in the HTTP response to "text/xml".
16133  * @constructor
16134  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16135  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16136  * will be used to make the request.
16137  */
16138 Roo.data.HttpProxy = function(conn){
16139     Roo.data.HttpProxy.superclass.constructor.call(this);
16140     // is conn a conn config or a real conn?
16141     this.conn = conn;
16142     this.useAjax = !conn || !conn.events;
16143   
16144 };
16145
16146 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16147     // thse are take from connection...
16148     
16149     /**
16150      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16151      */
16152     /**
16153      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16154      * extra parameters to each request made by this object. (defaults to undefined)
16155      */
16156     /**
16157      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16158      *  to each request made by this object. (defaults to undefined)
16159      */
16160     /**
16161      * @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)
16162      */
16163     /**
16164      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16165      */
16166      /**
16167      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16168      * @type Boolean
16169      */
16170   
16171
16172     /**
16173      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16174      * @type Boolean
16175      */
16176     /**
16177      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16178      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16179      * a finer-grained basis than the DataProxy events.
16180      */
16181     getConnection : function(){
16182         return this.useAjax ? Roo.Ajax : this.conn;
16183     },
16184
16185     /**
16186      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16187      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16188      * process that block using the passed callback.
16189      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16190      * for the request to the remote server.
16191      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16192      * object into a block of Roo.data.Records.
16193      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16194      * The function must be passed <ul>
16195      * <li>The Record block object</li>
16196      * <li>The "arg" argument from the load function</li>
16197      * <li>A boolean success indicator</li>
16198      * </ul>
16199      * @param {Object} scope The scope in which to call the callback
16200      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16201      */
16202     load : function(params, reader, callback, scope, arg){
16203         if(this.fireEvent("beforeload", this, params) !== false){
16204             var  o = {
16205                 params : params || {},
16206                 request: {
16207                     callback : callback,
16208                     scope : scope,
16209                     arg : arg
16210                 },
16211                 reader: reader,
16212                 callback : this.loadResponse,
16213                 scope: this
16214             };
16215             if(this.useAjax){
16216                 Roo.applyIf(o, this.conn);
16217                 if(this.activeRequest){
16218                     Roo.Ajax.abort(this.activeRequest);
16219                 }
16220                 this.activeRequest = Roo.Ajax.request(o);
16221             }else{
16222                 this.conn.request(o);
16223             }
16224         }else{
16225             callback.call(scope||this, null, arg, false);
16226         }
16227     },
16228
16229     // private
16230     loadResponse : function(o, success, response){
16231         delete this.activeRequest;
16232         if(!success){
16233             this.fireEvent("loadexception", this, o, response);
16234             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16235             return;
16236         }
16237         var result;
16238         try {
16239             result = o.reader.read(response);
16240         }catch(e){
16241             this.fireEvent("loadexception", this, o, response, e);
16242             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16243             return;
16244         }
16245         
16246         this.fireEvent("load", this, o, o.request.arg);
16247         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16248     },
16249
16250     // private
16251     update : function(dataSet){
16252
16253     },
16254
16255     // private
16256     updateResponse : function(dataSet){
16257
16258     }
16259 });/*
16260  * Based on:
16261  * Ext JS Library 1.1.1
16262  * Copyright(c) 2006-2007, Ext JS, LLC.
16263  *
16264  * Originally Released Under LGPL - original licence link has changed is not relivant.
16265  *
16266  * Fork - LGPL
16267  * <script type="text/javascript">
16268  */
16269
16270 /**
16271  * @class Roo.data.ScriptTagProxy
16272  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16273  * other than the originating domain of the running page.<br><br>
16274  * <p>
16275  * <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
16276  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16277  * <p>
16278  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16279  * source code that is used as the source inside a &lt;script> tag.<br><br>
16280  * <p>
16281  * In order for the browser to process the returned data, the server must wrap the data object
16282  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16283  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16284  * depending on whether the callback name was passed:
16285  * <p>
16286  * <pre><code>
16287 boolean scriptTag = false;
16288 String cb = request.getParameter("callback");
16289 if (cb != null) {
16290     scriptTag = true;
16291     response.setContentType("text/javascript");
16292 } else {
16293     response.setContentType("application/x-json");
16294 }
16295 Writer out = response.getWriter();
16296 if (scriptTag) {
16297     out.write(cb + "(");
16298 }
16299 out.print(dataBlock.toJsonString());
16300 if (scriptTag) {
16301     out.write(");");
16302 }
16303 </pre></code>
16304  *
16305  * @constructor
16306  * @param {Object} config A configuration object.
16307  */
16308 Roo.data.ScriptTagProxy = function(config){
16309     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16310     Roo.apply(this, config);
16311     this.head = document.getElementsByTagName("head")[0];
16312 };
16313
16314 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16315
16316 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16317     /**
16318      * @cfg {String} url The URL from which to request the data object.
16319      */
16320     /**
16321      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16322      */
16323     timeout : 30000,
16324     /**
16325      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16326      * the server the name of the callback function set up by the load call to process the returned data object.
16327      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16328      * javascript output which calls this named function passing the data object as its only parameter.
16329      */
16330     callbackParam : "callback",
16331     /**
16332      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16333      * name to the request.
16334      */
16335     nocache : true,
16336
16337     /**
16338      * Load data from the configured URL, read the data object into
16339      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16340      * process that block using the passed callback.
16341      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16342      * for the request to the remote server.
16343      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16344      * object into a block of Roo.data.Records.
16345      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16346      * The function must be passed <ul>
16347      * <li>The Record block object</li>
16348      * <li>The "arg" argument from the load function</li>
16349      * <li>A boolean success indicator</li>
16350      * </ul>
16351      * @param {Object} scope The scope in which to call the callback
16352      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16353      */
16354     load : function(params, reader, callback, scope, arg){
16355         if(this.fireEvent("beforeload", this, params) !== false){
16356
16357             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16358
16359             var url = this.url;
16360             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16361             if(this.nocache){
16362                 url += "&_dc=" + (new Date().getTime());
16363             }
16364             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16365             var trans = {
16366                 id : transId,
16367                 cb : "stcCallback"+transId,
16368                 scriptId : "stcScript"+transId,
16369                 params : params,
16370                 arg : arg,
16371                 url : url,
16372                 callback : callback,
16373                 scope : scope,
16374                 reader : reader
16375             };
16376             var conn = this;
16377
16378             window[trans.cb] = function(o){
16379                 conn.handleResponse(o, trans);
16380             };
16381
16382             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16383
16384             if(this.autoAbort !== false){
16385                 this.abort();
16386             }
16387
16388             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16389
16390             var script = document.createElement("script");
16391             script.setAttribute("src", url);
16392             script.setAttribute("type", "text/javascript");
16393             script.setAttribute("id", trans.scriptId);
16394             this.head.appendChild(script);
16395
16396             this.trans = trans;
16397         }else{
16398             callback.call(scope||this, null, arg, false);
16399         }
16400     },
16401
16402     // private
16403     isLoading : function(){
16404         return this.trans ? true : false;
16405     },
16406
16407     /**
16408      * Abort the current server request.
16409      */
16410     abort : function(){
16411         if(this.isLoading()){
16412             this.destroyTrans(this.trans);
16413         }
16414     },
16415
16416     // private
16417     destroyTrans : function(trans, isLoaded){
16418         this.head.removeChild(document.getElementById(trans.scriptId));
16419         clearTimeout(trans.timeoutId);
16420         if(isLoaded){
16421             window[trans.cb] = undefined;
16422             try{
16423                 delete window[trans.cb];
16424             }catch(e){}
16425         }else{
16426             // if hasn't been loaded, wait for load to remove it to prevent script error
16427             window[trans.cb] = function(){
16428                 window[trans.cb] = undefined;
16429                 try{
16430                     delete window[trans.cb];
16431                 }catch(e){}
16432             };
16433         }
16434     },
16435
16436     // private
16437     handleResponse : function(o, trans){
16438         this.trans = false;
16439         this.destroyTrans(trans, true);
16440         var result;
16441         try {
16442             result = trans.reader.readRecords(o);
16443         }catch(e){
16444             this.fireEvent("loadexception", this, o, trans.arg, e);
16445             trans.callback.call(trans.scope||window, null, trans.arg, false);
16446             return;
16447         }
16448         this.fireEvent("load", this, o, trans.arg);
16449         trans.callback.call(trans.scope||window, result, trans.arg, true);
16450     },
16451
16452     // private
16453     handleFailure : function(trans){
16454         this.trans = false;
16455         this.destroyTrans(trans, false);
16456         this.fireEvent("loadexception", this, null, trans.arg);
16457         trans.callback.call(trans.scope||window, null, trans.arg, false);
16458     }
16459 });/*
16460  * Based on:
16461  * Ext JS Library 1.1.1
16462  * Copyright(c) 2006-2007, Ext JS, LLC.
16463  *
16464  * Originally Released Under LGPL - original licence link has changed is not relivant.
16465  *
16466  * Fork - LGPL
16467  * <script type="text/javascript">
16468  */
16469
16470 /**
16471  * @class Roo.data.JsonReader
16472  * @extends Roo.data.DataReader
16473  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16474  * based on mappings in a provided Roo.data.Record constructor.
16475  * 
16476  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16477  * in the reply previously. 
16478  * 
16479  * <p>
16480  * Example code:
16481  * <pre><code>
16482 var RecordDef = Roo.data.Record.create([
16483     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16484     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16485 ]);
16486 var myReader = new Roo.data.JsonReader({
16487     totalProperty: "results",    // The property which contains the total dataset size (optional)
16488     root: "rows",                // The property which contains an Array of row objects
16489     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16490 }, RecordDef);
16491 </code></pre>
16492  * <p>
16493  * This would consume a JSON file like this:
16494  * <pre><code>
16495 { 'results': 2, 'rows': [
16496     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16497     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16498 }
16499 </code></pre>
16500  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16501  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16502  * paged from the remote server.
16503  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16504  * @cfg {String} root name of the property which contains the Array of row objects.
16505  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16506  * @cfg {Array} fields Array of field definition objects
16507  * @constructor
16508  * Create a new JsonReader
16509  * @param {Object} meta Metadata configuration options
16510  * @param {Object} recordType Either an Array of field definition objects,
16511  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16512  */
16513 Roo.data.JsonReader = function(meta, recordType){
16514     
16515     meta = meta || {};
16516     // set some defaults:
16517     Roo.applyIf(meta, {
16518         totalProperty: 'total',
16519         successProperty : 'success',
16520         root : 'data',
16521         id : 'id'
16522     });
16523     
16524     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16525 };
16526 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16527     
16528     readerType : 'Json',
16529     
16530     /**
16531      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16532      * Used by Store query builder to append _requestMeta to params.
16533      * 
16534      */
16535     metaFromRemote : false,
16536     /**
16537      * This method is only used by a DataProxy which has retrieved data from a remote server.
16538      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16539      * @return {Object} data A data block which is used by an Roo.data.Store object as
16540      * a cache of Roo.data.Records.
16541      */
16542     read : function(response){
16543         var json = response.responseText;
16544        
16545         var o = /* eval:var:o */ eval("("+json+")");
16546         if(!o) {
16547             throw {message: "JsonReader.read: Json object not found"};
16548         }
16549         
16550         if(o.metaData){
16551             
16552             delete this.ef;
16553             this.metaFromRemote = true;
16554             this.meta = o.metaData;
16555             this.recordType = Roo.data.Record.create(o.metaData.fields);
16556             this.onMetaChange(this.meta, this.recordType, o);
16557         }
16558         return this.readRecords(o);
16559     },
16560
16561     // private function a store will implement
16562     onMetaChange : function(meta, recordType, o){
16563
16564     },
16565
16566     /**
16567          * @ignore
16568          */
16569     simpleAccess: function(obj, subsc) {
16570         return obj[subsc];
16571     },
16572
16573         /**
16574          * @ignore
16575          */
16576     getJsonAccessor: function(){
16577         var re = /[\[\.]/;
16578         return function(expr) {
16579             try {
16580                 return(re.test(expr))
16581                     ? new Function("obj", "return obj." + expr)
16582                     : function(obj){
16583                         return obj[expr];
16584                     };
16585             } catch(e){}
16586             return Roo.emptyFn;
16587         };
16588     }(),
16589
16590     /**
16591      * Create a data block containing Roo.data.Records from an XML document.
16592      * @param {Object} o An object which contains an Array of row objects in the property specified
16593      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16594      * which contains the total size of the dataset.
16595      * @return {Object} data A data block which is used by an Roo.data.Store object as
16596      * a cache of Roo.data.Records.
16597      */
16598     readRecords : function(o){
16599         /**
16600          * After any data loads, the raw JSON data is available for further custom processing.
16601          * @type Object
16602          */
16603         this.o = o;
16604         var s = this.meta, Record = this.recordType,
16605             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16606
16607 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16608         if (!this.ef) {
16609             if(s.totalProperty) {
16610                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16611                 }
16612                 if(s.successProperty) {
16613                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16614                 }
16615                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16616                 if (s.id) {
16617                         var g = this.getJsonAccessor(s.id);
16618                         this.getId = function(rec) {
16619                                 var r = g(rec);  
16620                                 return (r === undefined || r === "") ? null : r;
16621                         };
16622                 } else {
16623                         this.getId = function(){return null;};
16624                 }
16625             this.ef = [];
16626             for(var jj = 0; jj < fl; jj++){
16627                 f = fi[jj];
16628                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16629                 this.ef[jj] = this.getJsonAccessor(map);
16630             }
16631         }
16632
16633         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16634         if(s.totalProperty){
16635             var vt = parseInt(this.getTotal(o), 10);
16636             if(!isNaN(vt)){
16637                 totalRecords = vt;
16638             }
16639         }
16640         if(s.successProperty){
16641             var vs = this.getSuccess(o);
16642             if(vs === false || vs === 'false'){
16643                 success = false;
16644             }
16645         }
16646         var records = [];
16647         for(var i = 0; i < c; i++){
16648                 var n = root[i];
16649             var values = {};
16650             var id = this.getId(n);
16651             for(var j = 0; j < fl; j++){
16652                 f = fi[j];
16653             var v = this.ef[j](n);
16654             if (!f.convert) {
16655                 Roo.log('missing convert for ' + f.name);
16656                 Roo.log(f);
16657                 continue;
16658             }
16659             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16660             }
16661             var record = new Record(values, id);
16662             record.json = n;
16663             records[i] = record;
16664         }
16665         return {
16666             raw : o,
16667             success : success,
16668             records : records,
16669             totalRecords : totalRecords
16670         };
16671     },
16672     // used when loading children.. @see loadDataFromChildren
16673     toLoadData: function(rec)
16674     {
16675         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16676         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16677         return { data : data, total : data.length };
16678         
16679     }
16680 });/*
16681  * Based on:
16682  * Ext JS Library 1.1.1
16683  * Copyright(c) 2006-2007, Ext JS, LLC.
16684  *
16685  * Originally Released Under LGPL - original licence link has changed is not relivant.
16686  *
16687  * Fork - LGPL
16688  * <script type="text/javascript">
16689  */
16690
16691 /**
16692  * @class Roo.data.ArrayReader
16693  * @extends Roo.data.DataReader
16694  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16695  * Each element of that Array represents a row of data fields. The
16696  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16697  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16698  * <p>
16699  * Example code:.
16700  * <pre><code>
16701 var RecordDef = Roo.data.Record.create([
16702     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16703     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16704 ]);
16705 var myReader = new Roo.data.ArrayReader({
16706     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16707 }, RecordDef);
16708 </code></pre>
16709  * <p>
16710  * This would consume an Array like this:
16711  * <pre><code>
16712 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16713   </code></pre>
16714  
16715  * @constructor
16716  * Create a new JsonReader
16717  * @param {Object} meta Metadata configuration options.
16718  * @param {Object|Array} recordType Either an Array of field definition objects
16719  * 
16720  * @cfg {Array} fields Array of field definition objects
16721  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16722  * as specified to {@link Roo.data.Record#create},
16723  * or an {@link Roo.data.Record} object
16724  *
16725  * 
16726  * created using {@link Roo.data.Record#create}.
16727  */
16728 Roo.data.ArrayReader = function(meta, recordType)
16729 {    
16730     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16731 };
16732
16733 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16734     
16735       /**
16736      * Create a data block containing Roo.data.Records from an XML document.
16737      * @param {Object} o An Array of row objects which represents the dataset.
16738      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16739      * a cache of Roo.data.Records.
16740      */
16741     readRecords : function(o)
16742     {
16743         var sid = this.meta ? this.meta.id : null;
16744         var recordType = this.recordType, fields = recordType.prototype.fields;
16745         var records = [];
16746         var root = o;
16747         for(var i = 0; i < root.length; i++){
16748             var n = root[i];
16749             var values = {};
16750             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16751             for(var j = 0, jlen = fields.length; j < jlen; j++){
16752                 var f = fields.items[j];
16753                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16754                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16755                 v = f.convert(v);
16756                 values[f.name] = v;
16757             }
16758             var record = new recordType(values, id);
16759             record.json = n;
16760             records[records.length] = record;
16761         }
16762         return {
16763             records : records,
16764             totalRecords : records.length
16765         };
16766     },
16767     // used when loading children.. @see loadDataFromChildren
16768     toLoadData: function(rec)
16769     {
16770         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16771         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16772         
16773     }
16774     
16775     
16776 });/*
16777  * - LGPL
16778  * * 
16779  */
16780
16781 /**
16782  * @class Roo.bootstrap.form.ComboBox
16783  * @extends Roo.bootstrap.form.TriggerField
16784  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16785  * @cfg {Boolean} append (true|false) default false
16786  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16787  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16788  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16789  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16790  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16791  * @cfg {Boolean} animate default true
16792  * @cfg {Boolean} emptyResultText only for touch device
16793  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16794  * @cfg {String} emptyTitle default ''
16795  * @cfg {Number} width fixed with? experimental
16796  * @constructor
16797  * Create a new ComboBox.
16798  * @param {Object} config Configuration options
16799  */
16800 Roo.bootstrap.form.ComboBox = function(config){
16801     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16802     this.addEvents({
16803         /**
16804          * @event expand
16805          * Fires when the dropdown list is expanded
16806         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16807         */
16808         'expand' : true,
16809         /**
16810          * @event collapse
16811          * Fires when the dropdown list is collapsed
16812         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16813         */
16814         'collapse' : true,
16815         /**
16816          * @event beforeselect
16817          * Fires before a list item is selected. Return false to cancel the selection.
16818         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16819         * @param {Roo.data.Record} record The data record returned from the underlying store
16820         * @param {Number} index The index of the selected item in the dropdown list
16821         */
16822         'beforeselect' : true,
16823         /**
16824          * @event select
16825          * Fires when a list item is selected
16826         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16827         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16828         * @param {Number} index The index of the selected item in the dropdown list
16829         */
16830         'select' : true,
16831         /**
16832          * @event beforequery
16833          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16834          * The event object passed has these properties:
16835         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16836         * @param {String} query The query
16837         * @param {Boolean} forceAll true to force "all" query
16838         * @param {Boolean} cancel true to cancel the query
16839         * @param {Object} e The query event object
16840         */
16841         'beforequery': true,
16842          /**
16843          * @event add
16844          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16845         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16846         */
16847         'add' : true,
16848         /**
16849          * @event edit
16850          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16851         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16852         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16853         */
16854         'edit' : true,
16855         /**
16856          * @event remove
16857          * Fires when the remove value from the combobox array
16858         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16859         */
16860         'remove' : true,
16861         /**
16862          * @event afterremove
16863          * Fires when the remove value from the combobox array
16864         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16865         */
16866         'afterremove' : true,
16867         /**
16868          * @event specialfilter
16869          * Fires when specialfilter
16870             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16871             */
16872         'specialfilter' : true,
16873         /**
16874          * @event tick
16875          * Fires when tick the element
16876             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16877             */
16878         'tick' : true,
16879         /**
16880          * @event touchviewdisplay
16881          * Fires when touch view require special display (default is using displayField)
16882             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16883             * @param {Object} cfg set html .
16884             */
16885         'touchviewdisplay' : true
16886         
16887     });
16888     
16889     this.item = [];
16890     this.tickItems = [];
16891     
16892     this.selectedIndex = -1;
16893     if(this.mode == 'local'){
16894         if(config.queryDelay === undefined){
16895             this.queryDelay = 10;
16896         }
16897         if(config.minChars === undefined){
16898             this.minChars = 0;
16899         }
16900     }
16901 };
16902
16903 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16904      
16905     /**
16906      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16907      * rendering into an Roo.Editor, defaults to false)
16908      */
16909     /**
16910      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16911      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16912      */
16913     /**
16914      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16915      */
16916     /**
16917      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16918      * the dropdown list (defaults to undefined, with no header element)
16919      */
16920
16921      /**
16922      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16923      */
16924      
16925      /**
16926      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16927      */
16928     listWidth: undefined,
16929     /**
16930      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16931      * mode = 'remote' or 'text' if mode = 'local')
16932      */
16933     displayField: undefined,
16934     
16935     /**
16936      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16937      * mode = 'remote' or 'value' if mode = 'local'). 
16938      * Note: use of a valueField requires the user make a selection
16939      * in order for a value to be mapped.
16940      */
16941     valueField: undefined,
16942     /**
16943      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16944      */
16945     modalTitle : '',
16946     
16947     /**
16948      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16949      * field's data value (defaults to the underlying DOM element's name)
16950      */
16951     hiddenName: undefined,
16952     /**
16953      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16954      */
16955     listClass: '',
16956     /**
16957      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16958      */
16959     selectedClass: 'active',
16960     
16961     /**
16962      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16963      */
16964     shadow:'sides',
16965     /**
16966      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16967      * anchor positions (defaults to 'tl-bl')
16968      */
16969     listAlign: 'tl-bl?',
16970     /**
16971      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16972      */
16973     maxHeight: 300,
16974     /**
16975      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16976      * query specified by the allQuery config option (defaults to 'query')
16977      */
16978     triggerAction: 'query',
16979     /**
16980      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16981      * (defaults to 4, does not apply if editable = false)
16982      */
16983     minChars : 4,
16984     /**
16985      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16986      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16987      */
16988     typeAhead: false,
16989     /**
16990      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16991      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16992      */
16993     queryDelay: 500,
16994     /**
16995      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16996      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16997      */
16998     pageSize: 0,
16999     /**
17000      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17001      * when editable = true (defaults to false)
17002      */
17003     selectOnFocus:false,
17004     /**
17005      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17006      */
17007     queryParam: 'query',
17008     /**
17009      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17010      * when mode = 'remote' (defaults to 'Loading...')
17011      */
17012     loadingText: 'Loading...',
17013     /**
17014      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17015      */
17016     resizable: false,
17017     /**
17018      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17019      */
17020     handleHeight : 8,
17021     /**
17022      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17023      * traditional select (defaults to true)
17024      */
17025     editable: true,
17026     /**
17027      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17028      */
17029     allQuery: '',
17030     /**
17031      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17032      */
17033     mode: 'remote',
17034     /**
17035      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17036      * listWidth has a higher value)
17037      */
17038     minListWidth : 70,
17039     /**
17040      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17041      * allow the user to set arbitrary text into the field (defaults to false)
17042      */
17043     forceSelection:false,
17044     /**
17045      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17046      * if typeAhead = true (defaults to 250)
17047      */
17048     typeAheadDelay : 250,
17049     /**
17050      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17051      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17052      */
17053     valueNotFoundText : undefined,
17054     /**
17055      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17056      */
17057     blockFocus : false,
17058     
17059     /**
17060      * @cfg {Boolean} disableClear Disable showing of clear button.
17061      */
17062     disableClear : false,
17063     /**
17064      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17065      */
17066     alwaysQuery : false,
17067     
17068     /**
17069      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17070      */
17071     multiple : false,
17072     
17073     /**
17074      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17075      */
17076     invalidClass : "has-warning",
17077     
17078     /**
17079      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17080      */
17081     validClass : "has-success",
17082     
17083     /**
17084      * @cfg {Boolean} specialFilter (true|false) special filter default false
17085      */
17086     specialFilter : false,
17087     
17088     /**
17089      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17090      */
17091     mobileTouchView : true,
17092     
17093     /**
17094      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17095      */
17096     useNativeIOS : false,
17097     
17098     /**
17099      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17100      */
17101     mobile_restrict_height : false,
17102     
17103     ios_options : false,
17104     
17105     //private
17106     addicon : false,
17107     editicon: false,
17108     
17109     page: 0,
17110     hasQuery: false,
17111     append: false,
17112     loadNext: false,
17113     autoFocus : true,
17114     tickable : false,
17115     btnPosition : 'right',
17116     triggerList : true,
17117     showToggleBtn : true,
17118     animate : true,
17119     emptyResultText: 'Empty',
17120     triggerText : 'Select',
17121     emptyTitle : '',
17122     width : false,
17123     
17124     // element that contains real text value.. (when hidden is used..)
17125     
17126     getAutoCreate : function()
17127     {   
17128         var cfg = false;
17129         //render
17130         /*
17131          * Render classic select for iso
17132          */
17133         
17134         if(Roo.isIOS && this.useNativeIOS){
17135             cfg = this.getAutoCreateNativeIOS();
17136             return cfg;
17137         }
17138         
17139         /*
17140          * Touch Devices
17141          */
17142         
17143         if(Roo.isTouch && this.mobileTouchView){
17144             cfg = this.getAutoCreateTouchView();
17145             return cfg;;
17146         }
17147         
17148         /*
17149          *  Normal ComboBox
17150          */
17151         if(!this.tickable){
17152             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17153             return cfg;
17154         }
17155         
17156         /*
17157          *  ComboBox with tickable selections
17158          */
17159              
17160         var align = this.labelAlign || this.parentLabelAlign();
17161         
17162         cfg = {
17163             cls : 'form-group roo-combobox-tickable' //input-group
17164         };
17165         
17166         var btn_text_select = '';
17167         var btn_text_done = '';
17168         var btn_text_cancel = '';
17169         
17170         if (this.btn_text_show) {
17171             btn_text_select = 'Select';
17172             btn_text_done = 'Done';
17173             btn_text_cancel = 'Cancel'; 
17174         }
17175         
17176         var buttons = {
17177             tag : 'div',
17178             cls : 'tickable-buttons',
17179             cn : [
17180                 {
17181                     tag : 'button',
17182                     type : 'button',
17183                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17184                     //html : this.triggerText
17185                     html: btn_text_select
17186                 },
17187                 {
17188                     tag : 'button',
17189                     type : 'button',
17190                     name : 'ok',
17191                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17192                     //html : 'Done'
17193                     html: btn_text_done
17194                 },
17195                 {
17196                     tag : 'button',
17197                     type : 'button',
17198                     name : 'cancel',
17199                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17200                     //html : 'Cancel'
17201                     html: btn_text_cancel
17202                 }
17203             ]
17204         };
17205         
17206         if(this.editable){
17207             buttons.cn.unshift({
17208                 tag: 'input',
17209                 cls: 'roo-select2-search-field-input'
17210             });
17211         }
17212         
17213         var _this = this;
17214         
17215         Roo.each(buttons.cn, function(c){
17216             if (_this.size) {
17217                 c.cls += ' btn-' + _this.size;
17218             }
17219
17220             if (_this.disabled) {
17221                 c.disabled = true;
17222             }
17223         });
17224         
17225         var box = {
17226             tag: 'div',
17227             style : 'display: contents',
17228             cn: [
17229                 {
17230                     tag: 'input',
17231                     type : 'hidden',
17232                     cls: 'form-hidden-field'
17233                 },
17234                 {
17235                     tag: 'ul',
17236                     cls: 'roo-select2-choices',
17237                     cn:[
17238                         {
17239                             tag: 'li',
17240                             cls: 'roo-select2-search-field',
17241                             cn: [
17242                                 buttons
17243                             ]
17244                         }
17245                     ]
17246                 }
17247             ]
17248         };
17249         
17250         var combobox = {
17251             cls: 'roo-select2-container input-group roo-select2-container-multi',
17252             cn: [
17253                 
17254                 box
17255 //                {
17256 //                    tag: 'ul',
17257 //                    cls: 'typeahead typeahead-long dropdown-menu',
17258 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17259 //                }
17260             ]
17261         };
17262         
17263         if(this.hasFeedback && !this.allowBlank){
17264             
17265             var feedback = {
17266                 tag: 'span',
17267                 cls: 'glyphicon form-control-feedback'
17268             };
17269
17270             combobox.cn.push(feedback);
17271         }
17272         
17273         
17274         
17275         var indicator = {
17276             tag : 'i',
17277             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17278             tooltip : 'This field is required'
17279         };
17280         if (Roo.bootstrap.version == 4) {
17281             indicator = {
17282                 tag : 'i',
17283                 style : 'display:none'
17284             };
17285         }
17286         if (align ==='left' && this.fieldLabel.length) {
17287             
17288             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17289             
17290             cfg.cn = [
17291                 indicator,
17292                 {
17293                     tag: 'label',
17294                     'for' :  id,
17295                     cls : 'control-label col-form-label',
17296                     html : this.fieldLabel
17297
17298                 },
17299                 {
17300                     cls : "", 
17301                     cn: [
17302                         combobox
17303                     ]
17304                 }
17305
17306             ];
17307             
17308             var labelCfg = cfg.cn[1];
17309             var contentCfg = cfg.cn[2];
17310             
17311
17312             if(this.indicatorpos == 'right'){
17313                 
17314                 cfg.cn = [
17315                     {
17316                         tag: 'label',
17317                         'for' :  id,
17318                         cls : 'control-label col-form-label',
17319                         cn : [
17320                             {
17321                                 tag : 'span',
17322                                 html : this.fieldLabel
17323                             },
17324                             indicator
17325                         ]
17326                     },
17327                     {
17328                         cls : "",
17329                         cn: [
17330                             combobox
17331                         ]
17332                     }
17333
17334                 ];
17335                 
17336                 
17337                 
17338                 labelCfg = cfg.cn[0];
17339                 contentCfg = cfg.cn[1];
17340             
17341             }
17342             
17343             if(this.labelWidth > 12){
17344                 labelCfg.style = "width: " + this.labelWidth + 'px';
17345             }
17346             if(this.width * 1 > 0){
17347                 contentCfg.style = "width: " + this.width + 'px';
17348             }
17349             if(this.labelWidth < 13 && this.labelmd == 0){
17350                 this.labelmd = this.labelWidth;
17351             }
17352             
17353             if(this.labellg > 0){
17354                 labelCfg.cls += ' col-lg-' + this.labellg;
17355                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17356             }
17357             
17358             if(this.labelmd > 0){
17359                 labelCfg.cls += ' col-md-' + this.labelmd;
17360                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17361             }
17362             
17363             if(this.labelsm > 0){
17364                 labelCfg.cls += ' col-sm-' + this.labelsm;
17365                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17366             }
17367             
17368             if(this.labelxs > 0){
17369                 labelCfg.cls += ' col-xs-' + this.labelxs;
17370                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17371             }
17372                 
17373                 
17374         } else if ( this.fieldLabel.length) {
17375 //                Roo.log(" label");
17376                  cfg.cn = [
17377                    indicator,
17378                     {
17379                         tag: 'label',
17380                         //cls : 'input-group-addon',
17381                         html : this.fieldLabel
17382                     },
17383                     combobox
17384                 ];
17385                 
17386                 if(this.indicatorpos == 'right'){
17387                     cfg.cn = [
17388                         {
17389                             tag: 'label',
17390                             //cls : 'input-group-addon',
17391                             html : this.fieldLabel
17392                         },
17393                         indicator,
17394                         combobox
17395                     ];
17396                     
17397                 }
17398
17399         } else {
17400             
17401 //                Roo.log(" no label && no align");
17402                 cfg = combobox
17403                      
17404                 
17405         }
17406          
17407         var settings=this;
17408         ['xs','sm','md','lg'].map(function(size){
17409             if (settings[size]) {
17410                 cfg.cls += ' col-' + size + '-' + settings[size];
17411             }
17412         });
17413         
17414         return cfg;
17415         
17416     },
17417     
17418     _initEventsCalled : false,
17419     
17420     // private
17421     initEvents: function()
17422     {   
17423         if (this._initEventsCalled) { // as we call render... prevent looping...
17424             return;
17425         }
17426         this._initEventsCalled = true;
17427         
17428         if (!this.store) {
17429             throw "can not find store for combo";
17430         }
17431         
17432         this.indicator = this.indicatorEl();
17433         
17434         this.store = Roo.factory(this.store, Roo.data);
17435         this.store.parent = this;
17436         
17437         // if we are building from html. then this element is so complex, that we can not really
17438         // use the rendered HTML.
17439         // so we have to trash and replace the previous code.
17440         if (Roo.XComponent.build_from_html) {
17441             // remove this element....
17442             var e = this.el.dom, k=0;
17443             while (e ) { e = e.previousSibling;  ++k;}
17444
17445             this.el.remove();
17446             
17447             this.el=false;
17448             this.rendered = false;
17449             
17450             this.render(this.parent().getChildContainer(true), k);
17451         }
17452         
17453         if(Roo.isIOS && this.useNativeIOS){
17454             this.initIOSView();
17455             return;
17456         }
17457         
17458         /*
17459          * Touch Devices
17460          */
17461         
17462         if(Roo.isTouch && this.mobileTouchView){
17463             this.initTouchView();
17464             return;
17465         }
17466         
17467         if(this.tickable){
17468             this.initTickableEvents();
17469             return;
17470         }
17471         
17472         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17473         
17474         if(this.hiddenName){
17475             
17476             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17477             
17478             this.hiddenField.dom.value =
17479                 this.hiddenValue !== undefined ? this.hiddenValue :
17480                 this.value !== undefined ? this.value : '';
17481
17482             // prevent input submission
17483             this.el.dom.removeAttribute('name');
17484             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17485              
17486              
17487         }
17488         //if(Roo.isGecko){
17489         //    this.el.dom.setAttribute('autocomplete', 'off');
17490         //}
17491         
17492         var cls = 'x-combo-list';
17493         
17494         //this.list = new Roo.Layer({
17495         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17496         //});
17497         
17498         var _this = this;
17499         
17500         (function(){
17501             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17502             _this.list.setWidth(lw);
17503         }).defer(100);
17504         
17505         this.list.on('mouseover', this.onViewOver, this);
17506         this.list.on('mousemove', this.onViewMove, this);
17507         this.list.on('scroll', this.onViewScroll, this);
17508         
17509         /*
17510         this.list.swallowEvent('mousewheel');
17511         this.assetHeight = 0;
17512
17513         if(this.title){
17514             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17515             this.assetHeight += this.header.getHeight();
17516         }
17517
17518         this.innerList = this.list.createChild({cls:cls+'-inner'});
17519         this.innerList.on('mouseover', this.onViewOver, this);
17520         this.innerList.on('mousemove', this.onViewMove, this);
17521         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17522         
17523         if(this.allowBlank && !this.pageSize && !this.disableClear){
17524             this.footer = this.list.createChild({cls:cls+'-ft'});
17525             this.pageTb = new Roo.Toolbar(this.footer);
17526            
17527         }
17528         if(this.pageSize){
17529             this.footer = this.list.createChild({cls:cls+'-ft'});
17530             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17531                     {pageSize: this.pageSize});
17532             
17533         }
17534         
17535         if (this.pageTb && this.allowBlank && !this.disableClear) {
17536             var _this = this;
17537             this.pageTb.add(new Roo.Toolbar.Fill(), {
17538                 cls: 'x-btn-icon x-btn-clear',
17539                 text: '&#160;',
17540                 handler: function()
17541                 {
17542                     _this.collapse();
17543                     _this.clearValue();
17544                     _this.onSelect(false, -1);
17545                 }
17546             });
17547         }
17548         if (this.footer) {
17549             this.assetHeight += this.footer.getHeight();
17550         }
17551         */
17552             
17553         if(!this.tpl){
17554             this.tpl = Roo.bootstrap.version == 4 ?
17555                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17556                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17557         }
17558
17559         this.view = new Roo.View(this.list, this.tpl, {
17560             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17561         });
17562         //this.view.wrapEl.setDisplayed(false);
17563         this.view.on('click', this.onViewClick, this);
17564         
17565         
17566         this.store.on('beforeload', this.onBeforeLoad, this);
17567         this.store.on('load', this.onLoad, this);
17568         this.store.on('loadexception', this.onLoadException, this);
17569         /*
17570         if(this.resizable){
17571             this.resizer = new Roo.Resizable(this.list,  {
17572                pinned:true, handles:'se'
17573             });
17574             this.resizer.on('resize', function(r, w, h){
17575                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17576                 this.listWidth = w;
17577                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17578                 this.restrictHeight();
17579             }, this);
17580             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17581         }
17582         */
17583         if(!this.editable){
17584             this.editable = true;
17585             this.setEditable(false);
17586         }
17587         
17588         /*
17589         
17590         if (typeof(this.events.add.listeners) != 'undefined') {
17591             
17592             this.addicon = this.wrap.createChild(
17593                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17594        
17595             this.addicon.on('click', function(e) {
17596                 this.fireEvent('add', this);
17597             }, this);
17598         }
17599         if (typeof(this.events.edit.listeners) != 'undefined') {
17600             
17601             this.editicon = this.wrap.createChild(
17602                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17603             if (this.addicon) {
17604                 this.editicon.setStyle('margin-left', '40px');
17605             }
17606             this.editicon.on('click', function(e) {
17607                 
17608                 // we fire even  if inothing is selected..
17609                 this.fireEvent('edit', this, this.lastData );
17610                 
17611             }, this);
17612         }
17613         */
17614         
17615         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17616             "up" : function(e){
17617                 this.inKeyMode = true;
17618                 this.selectPrev();
17619             },
17620
17621             "down" : function(e){
17622                 if(!this.isExpanded()){
17623                     this.onTriggerClick();
17624                 }else{
17625                     this.inKeyMode = true;
17626                     this.selectNext();
17627                 }
17628             },
17629
17630             "enter" : function(e){
17631 //                this.onViewClick();
17632                 //return true;
17633                 this.collapse();
17634                 
17635                 if(this.fireEvent("specialkey", this, e)){
17636                     this.onViewClick(false);
17637                 }
17638                 
17639                 return true;
17640             },
17641
17642             "esc" : function(e){
17643                 this.collapse();
17644             },
17645
17646             "tab" : function(e){
17647                 this.collapse();
17648                 
17649                 if(this.fireEvent("specialkey", this, e)){
17650                     this.onViewClick(false);
17651                 }
17652                 
17653                 return true;
17654             },
17655
17656             scope : this,
17657
17658             doRelay : function(foo, bar, hname){
17659                 if(hname == 'down' || this.scope.isExpanded()){
17660                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17661                 }
17662                 return true;
17663             },
17664
17665             forceKeyDown: true
17666         });
17667         
17668         
17669         this.queryDelay = Math.max(this.queryDelay || 10,
17670                 this.mode == 'local' ? 10 : 250);
17671         
17672         
17673         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17674         
17675         if(this.typeAhead){
17676             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17677         }
17678         if(this.editable !== false){
17679             this.inputEl().on("keyup", this.onKeyUp, this);
17680         }
17681         if(this.forceSelection){
17682             this.inputEl().on('blur', this.doForce, this);
17683         }
17684         
17685         if(this.multiple){
17686             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17687             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17688         }
17689     },
17690     
17691     initTickableEvents: function()
17692     {   
17693         this.createList();
17694         
17695         if(this.hiddenName){
17696             
17697             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17698             
17699             this.hiddenField.dom.value =
17700                 this.hiddenValue !== undefined ? this.hiddenValue :
17701                 this.value !== undefined ? this.value : '';
17702
17703             // prevent input submission
17704             this.el.dom.removeAttribute('name');
17705             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17706              
17707              
17708         }
17709         
17710 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17711         
17712         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17713         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17714         if(this.triggerList){
17715             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17716         }
17717          
17718         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17719         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17720         
17721         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17722         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17723         
17724         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17725         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17726         
17727         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17728         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17729         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17730         
17731         this.okBtn.hide();
17732         this.cancelBtn.hide();
17733         
17734         var _this = this;
17735         
17736         (function(){
17737             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17738             _this.list.setWidth(lw);
17739         }).defer(100);
17740         
17741         this.list.on('mouseover', this.onViewOver, this);
17742         this.list.on('mousemove', this.onViewMove, this);
17743         
17744         this.list.on('scroll', this.onViewScroll, this);
17745         
17746         if(!this.tpl){
17747             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17748                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17749         }
17750
17751         this.view = new Roo.View(this.list, this.tpl, {
17752             singleSelect:true,
17753             tickable:true,
17754             parent:this,
17755             store: this.store,
17756             selectedClass: this.selectedClass
17757         });
17758         
17759         //this.view.wrapEl.setDisplayed(false);
17760         this.view.on('click', this.onViewClick, this);
17761         
17762         
17763         
17764         this.store.on('beforeload', this.onBeforeLoad, this);
17765         this.store.on('load', this.onLoad, this);
17766         this.store.on('loadexception', this.onLoadException, this);
17767         
17768         if(this.editable){
17769             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17770                 "up" : function(e){
17771                     this.inKeyMode = true;
17772                     this.selectPrev();
17773                 },
17774
17775                 "down" : function(e){
17776                     this.inKeyMode = true;
17777                     this.selectNext();
17778                 },
17779
17780                 "enter" : function(e){
17781                     if(this.fireEvent("specialkey", this, e)){
17782                         this.onViewClick(false);
17783                     }
17784                     
17785                     return true;
17786                 },
17787
17788                 "esc" : function(e){
17789                     this.onTickableFooterButtonClick(e, false, false);
17790                 },
17791
17792                 "tab" : function(e){
17793                     this.fireEvent("specialkey", this, e);
17794                     
17795                     this.onTickableFooterButtonClick(e, false, false);
17796                     
17797                     return true;
17798                 },
17799
17800                 scope : this,
17801
17802                 doRelay : function(e, fn, key){
17803                     if(this.scope.isExpanded()){
17804                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17805                     }
17806                     return true;
17807                 },
17808
17809                 forceKeyDown: true
17810             });
17811         }
17812         
17813         this.queryDelay = Math.max(this.queryDelay || 10,
17814                 this.mode == 'local' ? 10 : 250);
17815         
17816         
17817         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17818         
17819         if(this.typeAhead){
17820             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17821         }
17822         
17823         if(this.editable !== false){
17824             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17825         }
17826         
17827         this.indicator = this.indicatorEl();
17828         
17829         if(this.indicator){
17830             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17831             this.indicator.hide();
17832         }
17833         
17834     },
17835
17836     onDestroy : function(){
17837         if(this.view){
17838             this.view.setStore(null);
17839             this.view.el.removeAllListeners();
17840             this.view.el.remove();
17841             this.view.purgeListeners();
17842         }
17843         if(this.list){
17844             this.list.dom.innerHTML  = '';
17845         }
17846         
17847         if(this.store){
17848             this.store.un('beforeload', this.onBeforeLoad, this);
17849             this.store.un('load', this.onLoad, this);
17850             this.store.un('loadexception', this.onLoadException, this);
17851         }
17852         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17853     },
17854
17855     // private
17856     fireKey : function(e){
17857         if(e.isNavKeyPress() && !this.list.isVisible()){
17858             this.fireEvent("specialkey", this, e);
17859         }
17860     },
17861
17862     // private
17863     onResize: function(w, h)
17864     {
17865         
17866         
17867 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17868 //        
17869 //        if(typeof w != 'number'){
17870 //            // we do not handle it!?!?
17871 //            return;
17872 //        }
17873 //        var tw = this.trigger.getWidth();
17874 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17875 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17876 //        var x = w - tw;
17877 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17878 //            
17879 //        //this.trigger.setStyle('left', x+'px');
17880 //        
17881 //        if(this.list && this.listWidth === undefined){
17882 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17883 //            this.list.setWidth(lw);
17884 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17885 //        }
17886         
17887     
17888         
17889     },
17890
17891     /**
17892      * Allow or prevent the user from directly editing the field text.  If false is passed,
17893      * the user will only be able to select from the items defined in the dropdown list.  This method
17894      * is the runtime equivalent of setting the 'editable' config option at config time.
17895      * @param {Boolean} value True to allow the user to directly edit the field text
17896      */
17897     setEditable : function(value){
17898         if(value == this.editable){
17899             return;
17900         }
17901         this.editable = value;
17902         if(!value){
17903             this.inputEl().dom.setAttribute('readOnly', true);
17904             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17905             this.inputEl().addClass('x-combo-noedit');
17906         }else{
17907             this.inputEl().dom.removeAttribute('readOnly');
17908             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17909             this.inputEl().removeClass('x-combo-noedit');
17910         }
17911     },
17912
17913     // private
17914     
17915     onBeforeLoad : function(combo,opts){
17916         if(!this.hasFocus){
17917             return;
17918         }
17919          if (!opts.add) {
17920             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17921          }
17922         this.restrictHeight();
17923         this.selectedIndex = -1;
17924     },
17925
17926     // private
17927     onLoad : function(){
17928         
17929         this.hasQuery = false;
17930         
17931         if(!this.hasFocus){
17932             return;
17933         }
17934         
17935         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17936             this.loading.hide();
17937         }
17938         
17939         if(this.store.getCount() > 0){
17940             
17941             this.expand();
17942             this.restrictHeight();
17943             if(this.lastQuery == this.allQuery){
17944                 if(this.editable && !this.tickable){
17945                     this.inputEl().dom.select();
17946                 }
17947                 
17948                 if(
17949                     !this.selectByValue(this.value, true) &&
17950                     this.autoFocus && 
17951                     (
17952                         !this.store.lastOptions ||
17953                         typeof(this.store.lastOptions.add) == 'undefined' || 
17954                         this.store.lastOptions.add != true
17955                     )
17956                 ){
17957                     this.select(0, true);
17958                 }
17959             }else{
17960                 if(this.autoFocus){
17961                     this.selectNext();
17962                 }
17963                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17964                     this.taTask.delay(this.typeAheadDelay);
17965                 }
17966             }
17967         }else{
17968             this.onEmptyResults();
17969         }
17970         
17971         //this.el.focus();
17972     },
17973     // private
17974     onLoadException : function()
17975     {
17976         this.hasQuery = false;
17977         
17978         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17979             this.loading.hide();
17980         }
17981         
17982         if(this.tickable && this.editable){
17983             return;
17984         }
17985         
17986         this.collapse();
17987         // only causes errors at present
17988         //Roo.log(this.store.reader.jsonData);
17989         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17990             // fixme
17991             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17992         //}
17993         
17994         
17995     },
17996     // private
17997     onTypeAhead : function(){
17998         if(this.store.getCount() > 0){
17999             var r = this.store.getAt(0);
18000             var newValue = r.data[this.displayField];
18001             var len = newValue.length;
18002             var selStart = this.getRawValue().length;
18003             
18004             if(selStart != len){
18005                 this.setRawValue(newValue);
18006                 this.selectText(selStart, newValue.length);
18007             }
18008         }
18009     },
18010
18011     // private
18012     onSelect : function(record, index){
18013         
18014         if(this.fireEvent('beforeselect', this, record, index) !== false){
18015         
18016             this.setFromData(index > -1 ? record.data : false);
18017             
18018             this.collapse();
18019             this.fireEvent('select', this, record, index);
18020         }
18021     },
18022
18023     /**
18024      * Returns the currently selected field value or empty string if no value is set.
18025      * @return {String} value The selected value
18026      */
18027     getValue : function()
18028     {
18029         if(Roo.isIOS && this.useNativeIOS){
18030             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18031         }
18032         
18033         if(this.multiple){
18034             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18035         }
18036         
18037         if(this.valueField){
18038             return typeof this.value != 'undefined' ? this.value : '';
18039         }else{
18040             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18041         }
18042     },
18043     
18044     getRawValue : function()
18045     {
18046         if(Roo.isIOS && this.useNativeIOS){
18047             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18048         }
18049         
18050         var v = this.inputEl().getValue();
18051         
18052         return v;
18053     },
18054
18055     /**
18056      * Clears any text/value currently set in the field
18057      */
18058     clearValue : function(){
18059         
18060         if(this.hiddenField){
18061             this.hiddenField.dom.value = '';
18062         }
18063         this.value = '';
18064         this.setRawValue('');
18065         this.lastSelectionText = '';
18066         this.lastData = false;
18067         
18068         var close = this.closeTriggerEl();
18069         
18070         if(close){
18071             close.hide();
18072         }
18073         
18074         this.validate();
18075         
18076     },
18077
18078     /**
18079      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18080      * will be displayed in the field.  If the value does not match the data value of an existing item,
18081      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18082      * Otherwise the field will be blank (although the value will still be set).
18083      * @param {String} value The value to match
18084      */
18085     setValue : function(v)
18086     {
18087         if(Roo.isIOS && this.useNativeIOS){
18088             this.setIOSValue(v);
18089             return;
18090         }
18091         
18092         if(this.multiple){
18093             this.syncValue();
18094             return;
18095         }
18096         
18097         var text = v;
18098         if(this.valueField){
18099             var r = this.findRecord(this.valueField, v);
18100             if(r){
18101                 text = r.data[this.displayField];
18102             }else if(this.valueNotFoundText !== undefined){
18103                 text = this.valueNotFoundText;
18104             }
18105         }
18106         this.lastSelectionText = text;
18107         if(this.hiddenField){
18108             this.hiddenField.dom.value = v;
18109         }
18110         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18111         this.value = v;
18112         
18113         var close = this.closeTriggerEl();
18114         
18115         if(close){
18116             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18117         }
18118         
18119         this.validate();
18120     },
18121     /**
18122      * @property {Object} the last set data for the element
18123      */
18124     
18125     lastData : false,
18126     /**
18127      * Sets the value of the field based on a object which is related to the record format for the store.
18128      * @param {Object} value the value to set as. or false on reset?
18129      */
18130     setFromData : function(o){
18131         
18132         if(this.multiple){
18133             this.addItem(o);
18134             return;
18135         }
18136             
18137         var dv = ''; // display value
18138         var vv = ''; // value value..
18139         this.lastData = o;
18140         if (this.displayField) {
18141             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18142         } else {
18143             // this is an error condition!!!
18144             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18145         }
18146         
18147         if(this.valueField){
18148             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18149         }
18150         
18151         var close = this.closeTriggerEl();
18152         
18153         if(close){
18154             if(dv.length || vv * 1 > 0){
18155                 close.show() ;
18156                 this.blockFocus=true;
18157             } else {
18158                 close.hide();
18159             }             
18160         }
18161         
18162         if(this.hiddenField){
18163             this.hiddenField.dom.value = vv;
18164             
18165             this.lastSelectionText = dv;
18166             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18167             this.value = vv;
18168             return;
18169         }
18170         // no hidden field.. - we store the value in 'value', but still display
18171         // display field!!!!
18172         this.lastSelectionText = dv;
18173         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18174         this.value = vv;
18175         
18176         
18177         
18178     },
18179     // private
18180     reset : function(){
18181         // overridden so that last data is reset..
18182         
18183         if(this.multiple){
18184             this.clearItem();
18185             return;
18186         }
18187         
18188         this.setValue(this.originalValue);
18189         //this.clearInvalid();
18190         this.lastData = false;
18191         if (this.view) {
18192             this.view.clearSelections();
18193         }
18194         
18195         this.validate();
18196     },
18197     // private
18198     findRecord : function(prop, value){
18199         var record;
18200         if(this.store.getCount() > 0){
18201             this.store.each(function(r){
18202                 if(r.data[prop] == value){
18203                     record = r;
18204                     return false;
18205                 }
18206                 return true;
18207             });
18208         }
18209         return record;
18210     },
18211     
18212     getName: function()
18213     {
18214         // returns hidden if it's set..
18215         if (!this.rendered) {return ''};
18216         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18217         
18218     },
18219     // private
18220     onViewMove : function(e, t){
18221         this.inKeyMode = false;
18222     },
18223
18224     // private
18225     onViewOver : function(e, t){
18226         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18227             return;
18228         }
18229         var item = this.view.findItemFromChild(t);
18230         
18231         if(item){
18232             var index = this.view.indexOf(item);
18233             this.select(index, false);
18234         }
18235     },
18236
18237     // private
18238     onViewClick : function(view, doFocus, el, e)
18239     {
18240         var index = this.view.getSelectedIndexes()[0];
18241         
18242         var r = this.store.getAt(index);
18243         
18244         if(this.tickable){
18245             
18246             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18247                 return;
18248             }
18249             
18250             var rm = false;
18251             var _this = this;
18252             
18253             Roo.each(this.tickItems, function(v,k){
18254                 
18255                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18256                     Roo.log(v);
18257                     _this.tickItems.splice(k, 1);
18258                     
18259                     if(typeof(e) == 'undefined' && view == false){
18260                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18261                     }
18262                     
18263                     rm = true;
18264                     return;
18265                 }
18266             });
18267             
18268             if(rm){
18269                 return;
18270             }
18271             
18272             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18273                 this.tickItems.push(r.data);
18274             }
18275             
18276             if(typeof(e) == 'undefined' && view == false){
18277                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18278             }
18279                     
18280             return;
18281         }
18282         
18283         if(r){
18284             this.onSelect(r, index);
18285         }
18286         if(doFocus !== false && !this.blockFocus){
18287             this.inputEl().focus();
18288         }
18289     },
18290
18291     // private
18292     restrictHeight : function(){
18293         //this.innerList.dom.style.height = '';
18294         //var inner = this.innerList.dom;
18295         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18296         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18297         //this.list.beginUpdate();
18298         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18299         this.list.alignTo(this.inputEl(), this.listAlign);
18300         this.list.alignTo(this.inputEl(), this.listAlign);
18301         //this.list.endUpdate();
18302     },
18303
18304     // private
18305     onEmptyResults : function(){
18306         
18307         if(this.tickable && this.editable){
18308             this.hasFocus = false;
18309             this.restrictHeight();
18310             return;
18311         }
18312         
18313         this.collapse();
18314     },
18315
18316     /**
18317      * Returns true if the dropdown list is expanded, else false.
18318      */
18319     isExpanded : function(){
18320         return this.list.isVisible();
18321     },
18322
18323     /**
18324      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18325      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18326      * @param {String} value The data value of the item to select
18327      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18328      * selected item if it is not currently in view (defaults to true)
18329      * @return {Boolean} True if the value matched an item in the list, else false
18330      */
18331     selectByValue : function(v, scrollIntoView){
18332         if(v !== undefined && v !== null){
18333             var r = this.findRecord(this.valueField || this.displayField, v);
18334             if(r){
18335                 this.select(this.store.indexOf(r), scrollIntoView);
18336                 return true;
18337             }
18338         }
18339         return false;
18340     },
18341
18342     /**
18343      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18344      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18345      * @param {Number} index The zero-based index of the list item to select
18346      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18347      * selected item if it is not currently in view (defaults to true)
18348      */
18349     select : function(index, scrollIntoView){
18350         this.selectedIndex = index;
18351         this.view.select(index);
18352         if(scrollIntoView !== false){
18353             var el = this.view.getNode(index);
18354             /*
18355              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18356              */
18357             if(el){
18358                 this.list.scrollChildIntoView(el, false);
18359             }
18360         }
18361     },
18362
18363     // private
18364     selectNext : function(){
18365         var ct = this.store.getCount();
18366         if(ct > 0){
18367             if(this.selectedIndex == -1){
18368                 this.select(0);
18369             }else if(this.selectedIndex < ct-1){
18370                 this.select(this.selectedIndex+1);
18371             }
18372         }
18373     },
18374
18375     // private
18376     selectPrev : function(){
18377         var ct = this.store.getCount();
18378         if(ct > 0){
18379             if(this.selectedIndex == -1){
18380                 this.select(0);
18381             }else if(this.selectedIndex != 0){
18382                 this.select(this.selectedIndex-1);
18383             }
18384         }
18385     },
18386
18387     // private
18388     onKeyUp : function(e){
18389         if(this.editable !== false && !e.isSpecialKey()){
18390             this.lastKey = e.getKey();
18391             this.dqTask.delay(this.queryDelay);
18392         }
18393     },
18394
18395     // private
18396     validateBlur : function(){
18397         return !this.list || !this.list.isVisible();   
18398     },
18399
18400     // private
18401     initQuery : function(){
18402         
18403         var v = this.getRawValue();
18404         
18405         if(this.tickable && this.editable){
18406             v = this.tickableInputEl().getValue();
18407         }
18408         
18409         this.doQuery(v);
18410     },
18411
18412     // private
18413     doForce : function(){
18414         if(this.inputEl().dom.value.length > 0){
18415             this.inputEl().dom.value =
18416                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18417              
18418         }
18419     },
18420
18421     /**
18422      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18423      * query allowing the query action to be canceled if needed.
18424      * @param {String} query The SQL query to execute
18425      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18426      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18427      * saved in the current store (defaults to false)
18428      */
18429     doQuery : function(q, forceAll){
18430         
18431         if(q === undefined || q === null){
18432             q = '';
18433         }
18434         var qe = {
18435             query: q,
18436             forceAll: forceAll,
18437             combo: this,
18438             cancel:false
18439         };
18440         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18441             return false;
18442         }
18443         q = qe.query;
18444         
18445         forceAll = qe.forceAll;
18446         if(forceAll === true || (q.length >= this.minChars)){
18447             
18448             this.hasQuery = true;
18449             
18450             if(this.lastQuery != q || this.alwaysQuery){
18451                 this.lastQuery = q;
18452                 if(this.mode == 'local'){
18453                     this.selectedIndex = -1;
18454                     if(forceAll){
18455                         this.store.clearFilter();
18456                     }else{
18457                         
18458                         if(this.specialFilter){
18459                             this.fireEvent('specialfilter', this);
18460                             this.onLoad();
18461                             return;
18462                         }
18463                         
18464                         this.store.filter(this.displayField, q);
18465                     }
18466                     
18467                     this.store.fireEvent("datachanged", this.store);
18468                     
18469                     this.onLoad();
18470                     
18471                     
18472                 }else{
18473                     
18474                     this.store.baseParams[this.queryParam] = q;
18475                     
18476                     var options = {params : this.getParams(q)};
18477                     
18478                     if(this.loadNext){
18479                         options.add = true;
18480                         options.params.start = this.page * this.pageSize;
18481                     }
18482                     
18483                     this.store.load(options);
18484                     
18485                     /*
18486                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18487                      *  we should expand the list on onLoad
18488                      *  so command out it
18489                      */
18490 //                    this.expand();
18491                 }
18492             }else{
18493                 this.selectedIndex = -1;
18494                 this.onLoad();   
18495             }
18496         }
18497         
18498         this.loadNext = false;
18499     },
18500     
18501     // private
18502     getParams : function(q){
18503         var p = {};
18504         //p[this.queryParam] = q;
18505         
18506         if(this.pageSize){
18507             p.start = 0;
18508             p.limit = this.pageSize;
18509         }
18510         return p;
18511     },
18512
18513     /**
18514      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18515      */
18516     collapse : function(){
18517         if(!this.isExpanded()){
18518             return;
18519         }
18520         
18521         this.list.hide();
18522         
18523         this.hasFocus = false;
18524         
18525         if(this.tickable){
18526             this.okBtn.hide();
18527             this.cancelBtn.hide();
18528             this.trigger.show();
18529             
18530             if(this.editable){
18531                 this.tickableInputEl().dom.value = '';
18532                 this.tickableInputEl().blur();
18533             }
18534             
18535         }
18536         
18537         Roo.get(document).un('mousedown', this.collapseIf, this);
18538         Roo.get(document).un('mousewheel', this.collapseIf, this);
18539         if (!this.editable) {
18540             Roo.get(document).un('keydown', this.listKeyPress, this);
18541         }
18542         this.fireEvent('collapse', this);
18543         
18544         this.validate();
18545     },
18546
18547     // private
18548     collapseIf : function(e){
18549         var in_combo  = e.within(this.el);
18550         var in_list =  e.within(this.list);
18551         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18552         
18553         if (in_combo || in_list || is_list) {
18554             //e.stopPropagation();
18555             return;
18556         }
18557         
18558         if(this.tickable){
18559             this.onTickableFooterButtonClick(e, false, false);
18560         }
18561
18562         this.collapse();
18563         
18564     },
18565
18566     /**
18567      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18568      */
18569     expand : function(){
18570        
18571         if(this.isExpanded() || !this.hasFocus){
18572             return;
18573         }
18574         
18575         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18576         this.list.setWidth(lw);
18577         
18578         Roo.log('expand');
18579         
18580         this.list.show();
18581         
18582         this.restrictHeight();
18583         
18584         if(this.tickable){
18585             
18586             this.tickItems = Roo.apply([], this.item);
18587             
18588             this.okBtn.show();
18589             this.cancelBtn.show();
18590             this.trigger.hide();
18591             
18592             if(this.editable){
18593                 this.tickableInputEl().focus();
18594             }
18595             
18596         }
18597         
18598         Roo.get(document).on('mousedown', this.collapseIf, this);
18599         Roo.get(document).on('mousewheel', this.collapseIf, this);
18600         if (!this.editable) {
18601             Roo.get(document).on('keydown', this.listKeyPress, this);
18602         }
18603         
18604         this.fireEvent('expand', this);
18605     },
18606
18607     // private
18608     // Implements the default empty TriggerField.onTriggerClick function
18609     onTriggerClick : function(e)
18610     {
18611         Roo.log('trigger click');
18612         
18613         if(this.disabled || !this.triggerList){
18614             return;
18615         }
18616         
18617         this.page = 0;
18618         this.loadNext = false;
18619         
18620         if(this.isExpanded()){
18621             this.collapse();
18622             if (!this.blockFocus) {
18623                 this.inputEl().focus();
18624             }
18625             
18626         }else {
18627             this.hasFocus = true;
18628             if(this.triggerAction == 'all') {
18629                 this.doQuery(this.allQuery, true);
18630             } else {
18631                 this.doQuery(this.getRawValue());
18632             }
18633             if (!this.blockFocus) {
18634                 this.inputEl().focus();
18635             }
18636         }
18637     },
18638     
18639     onTickableTriggerClick : function(e)
18640     {
18641         if(this.disabled){
18642             return;
18643         }
18644         
18645         this.page = 0;
18646         this.loadNext = false;
18647         this.hasFocus = true;
18648         
18649         if(this.triggerAction == 'all') {
18650             this.doQuery(this.allQuery, true);
18651         } else {
18652             this.doQuery(this.getRawValue());
18653         }
18654     },
18655     
18656     onSearchFieldClick : function(e)
18657     {
18658         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18659             this.onTickableFooterButtonClick(e, false, false);
18660             return;
18661         }
18662         
18663         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18664             return;
18665         }
18666         
18667         this.page = 0;
18668         this.loadNext = false;
18669         this.hasFocus = true;
18670         
18671         if(this.triggerAction == 'all') {
18672             this.doQuery(this.allQuery, true);
18673         } else {
18674             this.doQuery(this.getRawValue());
18675         }
18676     },
18677     
18678     listKeyPress : function(e)
18679     {
18680         //Roo.log('listkeypress');
18681         // scroll to first matching element based on key pres..
18682         if (e.isSpecialKey()) {
18683             return false;
18684         }
18685         var k = String.fromCharCode(e.getKey()).toUpperCase();
18686         //Roo.log(k);
18687         var match  = false;
18688         var csel = this.view.getSelectedNodes();
18689         var cselitem = false;
18690         if (csel.length) {
18691             var ix = this.view.indexOf(csel[0]);
18692             cselitem  = this.store.getAt(ix);
18693             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18694                 cselitem = false;
18695             }
18696             
18697         }
18698         
18699         this.store.each(function(v) { 
18700             if (cselitem) {
18701                 // start at existing selection.
18702                 if (cselitem.id == v.id) {
18703                     cselitem = false;
18704                 }
18705                 return true;
18706             }
18707                 
18708             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18709                 match = this.store.indexOf(v);
18710                 return false;
18711             }
18712             return true;
18713         }, this);
18714         
18715         if (match === false) {
18716             return true; // no more action?
18717         }
18718         // scroll to?
18719         this.view.select(match);
18720         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18721         sn.scrollIntoView(sn.dom.parentNode, false);
18722     },
18723     
18724     onViewScroll : function(e, t){
18725         
18726         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){
18727             return;
18728         }
18729         
18730         this.hasQuery = true;
18731         
18732         this.loading = this.list.select('.loading', true).first();
18733         
18734         if(this.loading === null){
18735             this.list.createChild({
18736                 tag: 'div',
18737                 cls: 'loading roo-select2-more-results roo-select2-active',
18738                 html: 'Loading more results...'
18739             });
18740             
18741             this.loading = this.list.select('.loading', true).first();
18742             
18743             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18744             
18745             this.loading.hide();
18746         }
18747         
18748         this.loading.show();
18749         
18750         var _combo = this;
18751         
18752         this.page++;
18753         this.loadNext = true;
18754         
18755         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18756         
18757         return;
18758     },
18759     
18760     addItem : function(o)
18761     {   
18762         var dv = ''; // display value
18763         
18764         if (this.displayField) {
18765             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18766         } else {
18767             // this is an error condition!!!
18768             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18769         }
18770         
18771         if(!dv.length){
18772             return;
18773         }
18774         
18775         var choice = this.choices.createChild({
18776             tag: 'li',
18777             cls: 'roo-select2-search-choice',
18778             cn: [
18779                 {
18780                     tag: 'div',
18781                     html: dv
18782                 },
18783                 {
18784                     tag: 'a',
18785                     href: '#',
18786                     cls: 'roo-select2-search-choice-close fa fa-times',
18787                     tabindex: '-1'
18788                 }
18789             ]
18790             
18791         }, this.searchField);
18792         
18793         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18794         
18795         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18796         
18797         this.item.push(o);
18798         
18799         this.lastData = o;
18800         
18801         this.syncValue();
18802         
18803         this.inputEl().dom.value = '';
18804         
18805         this.validate();
18806     },
18807     
18808     onRemoveItem : function(e, _self, o)
18809     {
18810         e.preventDefault();
18811         
18812         this.lastItem = Roo.apply([], this.item);
18813         
18814         var index = this.item.indexOf(o.data) * 1;
18815         
18816         if( index < 0){
18817             Roo.log('not this item?!');
18818             return;
18819         }
18820         
18821         this.item.splice(index, 1);
18822         o.item.remove();
18823         
18824         this.syncValue();
18825         
18826         this.fireEvent('remove', this, e);
18827         
18828         this.validate();
18829         
18830     },
18831     
18832     syncValue : function()
18833     {
18834         if(!this.item.length){
18835             this.clearValue();
18836             return;
18837         }
18838             
18839         var value = [];
18840         var _this = this;
18841         Roo.each(this.item, function(i){
18842             if(_this.valueField){
18843                 value.push(i[_this.valueField]);
18844                 return;
18845             }
18846
18847             value.push(i);
18848         });
18849
18850         this.value = value.join(',');
18851
18852         if(this.hiddenField){
18853             this.hiddenField.dom.value = this.value;
18854         }
18855         
18856         this.store.fireEvent("datachanged", this.store);
18857         
18858         this.validate();
18859     },
18860     
18861     clearItem : function()
18862     {
18863         if(!this.multiple){
18864             return;
18865         }
18866         
18867         this.item = [];
18868         
18869         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18870            c.remove();
18871         });
18872         
18873         this.syncValue();
18874         
18875         this.validate();
18876         
18877         if(this.tickable && !Roo.isTouch){
18878             this.view.refresh();
18879         }
18880     },
18881     
18882     inputEl: function ()
18883     {
18884         if(Roo.isIOS && this.useNativeIOS){
18885             return this.el.select('select.roo-ios-select', true).first();
18886         }
18887         
18888         if(Roo.isTouch && this.mobileTouchView){
18889             return this.el.select('input.form-control',true).first();
18890         }
18891         
18892         if(this.tickable){
18893             return this.searchField;
18894         }
18895         
18896         return this.el.select('input.form-control',true).first();
18897     },
18898     
18899     onTickableFooterButtonClick : function(e, btn, el)
18900     {
18901         e.preventDefault();
18902         
18903         this.lastItem = Roo.apply([], this.item);
18904         
18905         if(btn && btn.name == 'cancel'){
18906             this.tickItems = Roo.apply([], this.item);
18907             this.collapse();
18908             return;
18909         }
18910         
18911         this.clearItem();
18912         
18913         var _this = this;
18914         
18915         Roo.each(this.tickItems, function(o){
18916             _this.addItem(o);
18917         });
18918         
18919         this.collapse();
18920         
18921     },
18922     
18923     validate : function()
18924     {
18925         if(this.getVisibilityEl().hasClass('hidden')){
18926             return true;
18927         }
18928         
18929         var v = this.getRawValue();
18930         
18931         if(this.multiple){
18932             v = this.getValue();
18933         }
18934         
18935         if(this.disabled || this.allowBlank || v.length){
18936             this.markValid();
18937             return true;
18938         }
18939         
18940         this.markInvalid();
18941         return false;
18942     },
18943     
18944     tickableInputEl : function()
18945     {
18946         if(!this.tickable || !this.editable){
18947             return this.inputEl();
18948         }
18949         
18950         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18951     },
18952     
18953     
18954     getAutoCreateTouchView : function()
18955     {
18956         var id = Roo.id();
18957         
18958         var cfg = {
18959             cls: 'form-group' //input-group
18960         };
18961         
18962         var input =  {
18963             tag: 'input',
18964             id : id,
18965             type : this.inputType,
18966             cls : 'form-control x-combo-noedit',
18967             autocomplete: 'new-password',
18968             placeholder : this.placeholder || '',
18969             readonly : true
18970         };
18971         
18972         if (this.name) {
18973             input.name = this.name;
18974         }
18975         
18976         if (this.size) {
18977             input.cls += ' input-' + this.size;
18978         }
18979         
18980         if (this.disabled) {
18981             input.disabled = true;
18982         }
18983         
18984         var inputblock = {
18985             cls : 'roo-combobox-wrap',
18986             cn : [
18987                 input
18988             ]
18989         };
18990         
18991         if(this.before){
18992             inputblock.cls += ' input-group';
18993             
18994             inputblock.cn.unshift({
18995                 tag :'span',
18996                 cls : 'input-group-addon input-group-prepend input-group-text',
18997                 html : this.before
18998             });
18999         }
19000         
19001         if(this.removable && !this.multiple){
19002             inputblock.cls += ' roo-removable';
19003             
19004             inputblock.cn.push({
19005                 tag: 'button',
19006                 html : 'x',
19007                 cls : 'roo-combo-removable-btn close'
19008             });
19009         }
19010
19011         if(this.hasFeedback && !this.allowBlank){
19012             
19013             inputblock.cls += ' has-feedback';
19014             
19015             inputblock.cn.push({
19016                 tag: 'span',
19017                 cls: 'glyphicon form-control-feedback'
19018             });
19019             
19020         }
19021         
19022         if (this.after) {
19023             
19024             inputblock.cls += (this.before) ? '' : ' input-group';
19025             
19026             inputblock.cn.push({
19027                 tag :'span',
19028                 cls : 'input-group-addon input-group-append input-group-text',
19029                 html : this.after
19030             });
19031         }
19032
19033         
19034         var ibwrap = inputblock;
19035         
19036         if(this.multiple){
19037             ibwrap = {
19038                 tag: 'ul',
19039                 cls: 'roo-select2-choices',
19040                 cn:[
19041                     {
19042                         tag: 'li',
19043                         cls: 'roo-select2-search-field',
19044                         cn: [
19045
19046                             inputblock
19047                         ]
19048                     }
19049                 ]
19050             };
19051         
19052             
19053         }
19054         
19055         var combobox = {
19056             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19057             cn: [
19058                 {
19059                     tag: 'input',
19060                     type : 'hidden',
19061                     cls: 'form-hidden-field'
19062                 },
19063                 ibwrap
19064             ]
19065         };
19066         
19067         if(!this.multiple && this.showToggleBtn){
19068             
19069             var caret = {
19070                 cls: 'caret'
19071             };
19072             
19073             if (this.caret != false) {
19074                 caret = {
19075                      tag: 'i',
19076                      cls: 'fa fa-' + this.caret
19077                 };
19078                 
19079             }
19080             
19081             combobox.cn.push({
19082                 tag :'span',
19083                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19084                 cn : [
19085                     Roo.bootstrap.version == 3 ? caret : '',
19086                     {
19087                         tag: 'span',
19088                         cls: 'combobox-clear',
19089                         cn  : [
19090                             {
19091                                 tag : 'i',
19092                                 cls: 'icon-remove'
19093                             }
19094                         ]
19095                     }
19096                 ]
19097
19098             })
19099         }
19100         
19101         if(this.multiple){
19102             combobox.cls += ' roo-select2-container-multi';
19103         }
19104         
19105         var required =  this.allowBlank ?  {
19106                     tag : 'i',
19107                     style: 'display: none'
19108                 } : {
19109                    tag : 'i',
19110                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19111                    tooltip : 'This field is required'
19112                 };
19113         
19114         var align = this.labelAlign || this.parentLabelAlign();
19115         
19116         if (align ==='left' && this.fieldLabel.length) {
19117
19118             cfg.cn = [
19119                 required,
19120                 {
19121                     tag: 'label',
19122                     cls : 'control-label col-form-label',
19123                     html : this.fieldLabel
19124
19125                 },
19126                 {
19127                     cls : 'roo-combobox-wrap ', 
19128                     cn: [
19129                         combobox
19130                     ]
19131                 }
19132             ];
19133             
19134             var labelCfg = cfg.cn[1];
19135             var contentCfg = cfg.cn[2];
19136             
19137
19138             if(this.indicatorpos == 'right'){
19139                 cfg.cn = [
19140                     {
19141                         tag: 'label',
19142                         'for' :  id,
19143                         cls : 'control-label col-form-label',
19144                         cn : [
19145                             {
19146                                 tag : 'span',
19147                                 html : this.fieldLabel
19148                             },
19149                             required
19150                         ]
19151                     },
19152                     {
19153                         cls : "roo-combobox-wrap ",
19154                         cn: [
19155                             combobox
19156                         ]
19157                     }
19158
19159                 ];
19160                 
19161                 labelCfg = cfg.cn[0];
19162                 contentCfg = cfg.cn[1];
19163             }
19164             
19165            
19166             
19167             if(this.labelWidth > 12){
19168                 labelCfg.style = "width: " + this.labelWidth + 'px';
19169             }
19170            
19171             if(this.labelWidth < 13 && this.labelmd == 0){
19172                 this.labelmd = this.labelWidth;
19173             }
19174             
19175             if(this.labellg > 0){
19176                 labelCfg.cls += ' col-lg-' + this.labellg;
19177                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19178             }
19179             
19180             if(this.labelmd > 0){
19181                 labelCfg.cls += ' col-md-' + this.labelmd;
19182                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19183             }
19184             
19185             if(this.labelsm > 0){
19186                 labelCfg.cls += ' col-sm-' + this.labelsm;
19187                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19188             }
19189             
19190             if(this.labelxs > 0){
19191                 labelCfg.cls += ' col-xs-' + this.labelxs;
19192                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19193             }
19194                 
19195                 
19196         } else if ( this.fieldLabel.length) {
19197             cfg.cn = [
19198                required,
19199                 {
19200                     tag: 'label',
19201                     cls : 'control-label',
19202                     html : this.fieldLabel
19203
19204                 },
19205                 {
19206                     cls : '', 
19207                     cn: [
19208                         combobox
19209                     ]
19210                 }
19211             ];
19212             
19213             if(this.indicatorpos == 'right'){
19214                 cfg.cn = [
19215                     {
19216                         tag: 'label',
19217                         cls : 'control-label',
19218                         html : this.fieldLabel,
19219                         cn : [
19220                             required
19221                         ]
19222                     },
19223                     {
19224                         cls : '', 
19225                         cn: [
19226                             combobox
19227                         ]
19228                     }
19229                 ];
19230             }
19231         } else {
19232             cfg.cn = combobox;    
19233         }
19234         
19235         
19236         var settings = this;
19237         
19238         ['xs','sm','md','lg'].map(function(size){
19239             if (settings[size]) {
19240                 cfg.cls += ' col-' + size + '-' + settings[size];
19241             }
19242         });
19243         
19244         return cfg;
19245     },
19246     
19247     initTouchView : function()
19248     {
19249         this.renderTouchView();
19250         
19251         this.touchViewEl.on('scroll', function(){
19252             this.el.dom.scrollTop = 0;
19253         }, this);
19254         
19255         this.originalValue = this.getValue();
19256         
19257         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19258         
19259         this.inputEl().on("click", this.showTouchView, this);
19260         if (this.triggerEl) {
19261             this.triggerEl.on("click", this.showTouchView, this);
19262         }
19263         
19264         
19265         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19266         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19267         
19268         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19269         
19270         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19271         this.store.on('load', this.onTouchViewLoad, this);
19272         this.store.on('loadexception', this.onTouchViewLoadException, this);
19273         
19274         if(this.hiddenName){
19275             
19276             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19277             
19278             this.hiddenField.dom.value =
19279                 this.hiddenValue !== undefined ? this.hiddenValue :
19280                 this.value !== undefined ? this.value : '';
19281         
19282             this.el.dom.removeAttribute('name');
19283             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19284         }
19285         
19286         if(this.multiple){
19287             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19288             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19289         }
19290         
19291         if(this.removable && !this.multiple){
19292             var close = this.closeTriggerEl();
19293             if(close){
19294                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19295                 close.on('click', this.removeBtnClick, this, close);
19296             }
19297         }
19298         /*
19299          * fix the bug in Safari iOS8
19300          */
19301         this.inputEl().on("focus", function(e){
19302             document.activeElement.blur();
19303         }, this);
19304         
19305         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19306         
19307         return;
19308         
19309         
19310     },
19311     
19312     renderTouchView : function()
19313     {
19314         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19315         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19316         
19317         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19318         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19319         
19320         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19321         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19322         this.touchViewBodyEl.setStyle('overflow', 'auto');
19323         
19324         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19325         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19326         
19327         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19328         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19329         
19330     },
19331     
19332     showTouchView : function()
19333     {
19334         if(this.disabled){
19335             return;
19336         }
19337         
19338         this.touchViewHeaderEl.hide();
19339
19340         if(this.modalTitle.length){
19341             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19342             this.touchViewHeaderEl.show();
19343         }
19344
19345         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19346         this.touchViewEl.show();
19347
19348         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19349         
19350         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19351         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19352
19353         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19354
19355         if(this.modalTitle.length){
19356             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19357         }
19358         
19359         this.touchViewBodyEl.setHeight(bodyHeight);
19360
19361         if(this.animate){
19362             var _this = this;
19363             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19364         }else{
19365             this.touchViewEl.addClass(['in','show']);
19366         }
19367         
19368         if(this._touchViewMask){
19369             Roo.get(document.body).addClass("x-body-masked");
19370             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19371             this._touchViewMask.setStyle('z-index', 10000);
19372             this._touchViewMask.addClass('show');
19373         }
19374         
19375         this.doTouchViewQuery();
19376         
19377     },
19378     
19379     hideTouchView : function()
19380     {
19381         this.touchViewEl.removeClass(['in','show']);
19382
19383         if(this.animate){
19384             var _this = this;
19385             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19386         }else{
19387             this.touchViewEl.setStyle('display', 'none');
19388         }
19389         
19390         if(this._touchViewMask){
19391             this._touchViewMask.removeClass('show');
19392             Roo.get(document.body).removeClass("x-body-masked");
19393         }
19394     },
19395     
19396     setTouchViewValue : function()
19397     {
19398         if(this.multiple){
19399             this.clearItem();
19400         
19401             var _this = this;
19402
19403             Roo.each(this.tickItems, function(o){
19404                 this.addItem(o);
19405             }, this);
19406         }
19407         
19408         this.hideTouchView();
19409     },
19410     
19411     doTouchViewQuery : function()
19412     {
19413         var qe = {
19414             query: '',
19415             forceAll: true,
19416             combo: this,
19417             cancel:false
19418         };
19419         
19420         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19421             return false;
19422         }
19423         
19424         if(!this.alwaysQuery || this.mode == 'local'){
19425             this.onTouchViewLoad();
19426             return;
19427         }
19428         
19429         this.store.load();
19430     },
19431     
19432     onTouchViewBeforeLoad : function(combo,opts)
19433     {
19434         return;
19435     },
19436
19437     // private
19438     onTouchViewLoad : function()
19439     {
19440         if(this.store.getCount() < 1){
19441             this.onTouchViewEmptyResults();
19442             return;
19443         }
19444         
19445         this.clearTouchView();
19446         
19447         var rawValue = this.getRawValue();
19448         
19449         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19450         
19451         this.tickItems = [];
19452         
19453         this.store.data.each(function(d, rowIndex){
19454             var row = this.touchViewListGroup.createChild(template);
19455             
19456             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19457                 row.addClass(d.data.cls);
19458             }
19459             
19460             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19461                 var cfg = {
19462                     data : d.data,
19463                     html : d.data[this.displayField]
19464                 };
19465                 
19466                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19467                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19468                 }
19469             }
19470             row.removeClass('selected');
19471             if(!this.multiple && this.valueField &&
19472                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19473             {
19474                 // radio buttons..
19475                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19476                 row.addClass('selected');
19477             }
19478             
19479             if(this.multiple && this.valueField &&
19480                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19481             {
19482                 
19483                 // checkboxes...
19484                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19485                 this.tickItems.push(d.data);
19486             }
19487             
19488             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19489             
19490         }, this);
19491         
19492         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19493         
19494         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19495
19496         if(this.modalTitle.length){
19497             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19498         }
19499
19500         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19501         
19502         if(this.mobile_restrict_height && listHeight < bodyHeight){
19503             this.touchViewBodyEl.setHeight(listHeight);
19504         }
19505         
19506         var _this = this;
19507         
19508         if(firstChecked && listHeight > bodyHeight){
19509             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19510         }
19511         
19512     },
19513     
19514     onTouchViewLoadException : function()
19515     {
19516         this.hideTouchView();
19517     },
19518     
19519     onTouchViewEmptyResults : function()
19520     {
19521         this.clearTouchView();
19522         
19523         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19524         
19525         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19526         
19527     },
19528     
19529     clearTouchView : function()
19530     {
19531         this.touchViewListGroup.dom.innerHTML = '';
19532     },
19533     
19534     onTouchViewClick : function(e, el, o)
19535     {
19536         e.preventDefault();
19537         
19538         var row = o.row;
19539         var rowIndex = o.rowIndex;
19540         
19541         var r = this.store.getAt(rowIndex);
19542         
19543         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19544             
19545             if(!this.multiple){
19546                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19547                     c.dom.removeAttribute('checked');
19548                 }, this);
19549
19550                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19551
19552                 this.setFromData(r.data);
19553
19554                 var close = this.closeTriggerEl();
19555
19556                 if(close){
19557                     close.show();
19558                 }
19559
19560                 this.hideTouchView();
19561
19562                 this.fireEvent('select', this, r, rowIndex);
19563
19564                 return;
19565             }
19566
19567             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19568                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19569                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19570                 return;
19571             }
19572
19573             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19574             this.addItem(r.data);
19575             this.tickItems.push(r.data);
19576         }
19577     },
19578     
19579     getAutoCreateNativeIOS : function()
19580     {
19581         var cfg = {
19582             cls: 'form-group' //input-group,
19583         };
19584         
19585         var combobox =  {
19586             tag: 'select',
19587             cls : 'roo-ios-select'
19588         };
19589         
19590         if (this.name) {
19591             combobox.name = this.name;
19592         }
19593         
19594         if (this.disabled) {
19595             combobox.disabled = true;
19596         }
19597         
19598         var settings = this;
19599         
19600         ['xs','sm','md','lg'].map(function(size){
19601             if (settings[size]) {
19602                 cfg.cls += ' col-' + size + '-' + settings[size];
19603             }
19604         });
19605         
19606         cfg.cn = combobox;
19607         
19608         return cfg;
19609         
19610     },
19611     
19612     initIOSView : function()
19613     {
19614         this.store.on('load', this.onIOSViewLoad, this);
19615         
19616         return;
19617     },
19618     
19619     onIOSViewLoad : function()
19620     {
19621         if(this.store.getCount() < 1){
19622             return;
19623         }
19624         
19625         this.clearIOSView();
19626         
19627         if(this.allowBlank) {
19628             
19629             var default_text = '-- SELECT --';
19630             
19631             if(this.placeholder.length){
19632                 default_text = this.placeholder;
19633             }
19634             
19635             if(this.emptyTitle.length){
19636                 default_text += ' - ' + this.emptyTitle + ' -';
19637             }
19638             
19639             var opt = this.inputEl().createChild({
19640                 tag: 'option',
19641                 value : 0,
19642                 html : default_text
19643             });
19644             
19645             var o = {};
19646             o[this.valueField] = 0;
19647             o[this.displayField] = default_text;
19648             
19649             this.ios_options.push({
19650                 data : o,
19651                 el : opt
19652             });
19653             
19654         }
19655         
19656         this.store.data.each(function(d, rowIndex){
19657             
19658             var html = '';
19659             
19660             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19661                 html = d.data[this.displayField];
19662             }
19663             
19664             var value = '';
19665             
19666             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19667                 value = d.data[this.valueField];
19668             }
19669             
19670             var option = {
19671                 tag: 'option',
19672                 value : value,
19673                 html : html
19674             };
19675             
19676             if(this.value == d.data[this.valueField]){
19677                 option['selected'] = true;
19678             }
19679             
19680             var opt = this.inputEl().createChild(option);
19681             
19682             this.ios_options.push({
19683                 data : d.data,
19684                 el : opt
19685             });
19686             
19687         }, this);
19688         
19689         this.inputEl().on('change', function(){
19690            this.fireEvent('select', this);
19691         }, this);
19692         
19693     },
19694     
19695     clearIOSView: function()
19696     {
19697         this.inputEl().dom.innerHTML = '';
19698         
19699         this.ios_options = [];
19700     },
19701     
19702     setIOSValue: function(v)
19703     {
19704         this.value = v;
19705         
19706         if(!this.ios_options){
19707             return;
19708         }
19709         
19710         Roo.each(this.ios_options, function(opts){
19711            
19712            opts.el.dom.removeAttribute('selected');
19713            
19714            if(opts.data[this.valueField] != v){
19715                return;
19716            }
19717            
19718            opts.el.dom.setAttribute('selected', true);
19719            
19720         }, this);
19721     }
19722
19723     /** 
19724     * @cfg {Boolean} grow 
19725     * @hide 
19726     */
19727     /** 
19728     * @cfg {Number} growMin 
19729     * @hide 
19730     */
19731     /** 
19732     * @cfg {Number} growMax 
19733     * @hide 
19734     */
19735     /**
19736      * @hide
19737      * @method autoSize
19738      */
19739 });
19740
19741 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19742     
19743     header : {
19744         tag: 'div',
19745         cls: 'modal-header',
19746         cn: [
19747             {
19748                 tag: 'h4',
19749                 cls: 'modal-title'
19750             }
19751         ]
19752     },
19753     
19754     body : {
19755         tag: 'div',
19756         cls: 'modal-body',
19757         cn: [
19758             {
19759                 tag: 'ul',
19760                 cls: 'list-group'
19761             }
19762         ]
19763     },
19764     
19765     listItemRadio : {
19766         tag: 'li',
19767         cls: 'list-group-item',
19768         cn: [
19769             {
19770                 tag: 'span',
19771                 cls: 'roo-combobox-list-group-item-value'
19772             },
19773             {
19774                 tag: 'div',
19775                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19776                 cn: [
19777                     {
19778                         tag: 'input',
19779                         type: 'radio'
19780                     },
19781                     {
19782                         tag: 'label'
19783                     }
19784                 ]
19785             }
19786         ]
19787     },
19788     
19789     listItemCheckbox : {
19790         tag: 'li',
19791         cls: 'list-group-item',
19792         cn: [
19793             {
19794                 tag: 'span',
19795                 cls: 'roo-combobox-list-group-item-value'
19796             },
19797             {
19798                 tag: 'div',
19799                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19800                 cn: [
19801                     {
19802                         tag: 'input',
19803                         type: 'checkbox'
19804                     },
19805                     {
19806                         tag: 'label'
19807                     }
19808                 ]
19809             }
19810         ]
19811     },
19812     
19813     emptyResult : {
19814         tag: 'div',
19815         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19816     },
19817     
19818     footer : {
19819         tag: 'div',
19820         cls: 'modal-footer',
19821         cn: [
19822             {
19823                 tag: 'div',
19824                 cls: 'row',
19825                 cn: [
19826                     {
19827                         tag: 'div',
19828                         cls: 'col-xs-6 text-left',
19829                         cn: {
19830                             tag: 'button',
19831                             cls: 'btn btn-danger roo-touch-view-cancel',
19832                             html: 'Cancel'
19833                         }
19834                     },
19835                     {
19836                         tag: 'div',
19837                         cls: 'col-xs-6 text-right',
19838                         cn: {
19839                             tag: 'button',
19840                             cls: 'btn btn-success roo-touch-view-ok',
19841                             html: 'OK'
19842                         }
19843                     }
19844                 ]
19845             }
19846         ]
19847         
19848     }
19849 });
19850
19851 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19852     
19853     touchViewTemplate : {
19854         tag: 'div',
19855         cls: 'modal fade roo-combobox-touch-view',
19856         cn: [
19857             {
19858                 tag: 'div',
19859                 cls: 'modal-dialog',
19860                 style : 'position:fixed', // we have to fix position....
19861                 cn: [
19862                     {
19863                         tag: 'div',
19864                         cls: 'modal-content',
19865                         cn: [
19866                             Roo.bootstrap.form.ComboBox.header,
19867                             Roo.bootstrap.form.ComboBox.body,
19868                             Roo.bootstrap.form.ComboBox.footer
19869                         ]
19870                     }
19871                 ]
19872             }
19873         ]
19874     }
19875 });/*
19876  * Based on:
19877  * Ext JS Library 1.1.1
19878  * Copyright(c) 2006-2007, Ext JS, LLC.
19879  *
19880  * Originally Released Under LGPL - original licence link has changed is not relivant.
19881  *
19882  * Fork - LGPL
19883  * <script type="text/javascript">
19884  */
19885
19886 /**
19887  * @class Roo.View
19888  * @extends Roo.util.Observable
19889  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19890  * This class also supports single and multi selection modes. <br>
19891  * Create a data model bound view:
19892  <pre><code>
19893  var store = new Roo.data.Store(...);
19894
19895  var view = new Roo.View({
19896     el : "my-element",
19897     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19898  
19899     singleSelect: true,
19900     selectedClass: "ydataview-selected",
19901     store: store
19902  });
19903
19904  // listen for node click?
19905  view.on("click", function(vw, index, node, e){
19906  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19907  });
19908
19909  // load XML data
19910  dataModel.load("foobar.xml");
19911  </code></pre>
19912  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19913  * <br><br>
19914  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19915  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19916  * 
19917  * Note: old style constructor is still suported (container, template, config)
19918  * 
19919  * @constructor
19920  * Create a new View
19921  * @param {Object} config The config object
19922  * 
19923  */
19924 Roo.View = function(config, depreciated_tpl, depreciated_config){
19925     
19926     this.parent = false;
19927     
19928     if (typeof(depreciated_tpl) == 'undefined') {
19929         // new way.. - universal constructor.
19930         Roo.apply(this, config);
19931         this.el  = Roo.get(this.el);
19932     } else {
19933         // old format..
19934         this.el  = Roo.get(config);
19935         this.tpl = depreciated_tpl;
19936         Roo.apply(this, depreciated_config);
19937     }
19938     this.wrapEl  = this.el.wrap().wrap();
19939     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19940     
19941     
19942     if(typeof(this.tpl) == "string"){
19943         this.tpl = new Roo.Template(this.tpl);
19944     } else {
19945         // support xtype ctors..
19946         this.tpl = new Roo.factory(this.tpl, Roo);
19947     }
19948     
19949     
19950     this.tpl.compile();
19951     
19952     /** @private */
19953     this.addEvents({
19954         /**
19955          * @event beforeclick
19956          * Fires before a click is processed. Returns false to cancel the default action.
19957          * @param {Roo.View} this
19958          * @param {Number} index The index of the target node
19959          * @param {HTMLElement} node The target node
19960          * @param {Roo.EventObject} e The raw event object
19961          */
19962             "beforeclick" : true,
19963         /**
19964          * @event click
19965          * Fires when a template node is clicked.
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             "click" : true,
19972         /**
19973          * @event dblclick
19974          * Fires when a template node is double 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             "dblclick" : true,
19981         /**
19982          * @event contextmenu
19983          * Fires when a template node is right 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             "contextmenu" : true,
19990         /**
19991          * @event selectionchange
19992          * Fires when the selected nodes change.
19993          * @param {Roo.View} this
19994          * @param {Array} selections Array of the selected nodes
19995          */
19996             "selectionchange" : true,
19997     
19998         /**
19999          * @event beforeselect
20000          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20001          * @param {Roo.View} this
20002          * @param {HTMLElement} node The node to be selected
20003          * @param {Array} selections Array of currently selected nodes
20004          */
20005             "beforeselect" : true,
20006         /**
20007          * @event preparedata
20008          * Fires on every row to render, to allow you to change the data.
20009          * @param {Roo.View} this
20010          * @param {Object} data to be rendered (change this)
20011          */
20012           "preparedata" : true
20013           
20014           
20015         });
20016
20017
20018
20019     this.el.on({
20020         "click": this.onClick,
20021         "dblclick": this.onDblClick,
20022         "contextmenu": this.onContextMenu,
20023         scope:this
20024     });
20025
20026     this.selections = [];
20027     this.nodes = [];
20028     this.cmp = new Roo.CompositeElementLite([]);
20029     if(this.store){
20030         this.store = Roo.factory(this.store, Roo.data);
20031         this.setStore(this.store, true);
20032     }
20033     
20034     if ( this.footer && this.footer.xtype) {
20035            
20036          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20037         
20038         this.footer.dataSource = this.store;
20039         this.footer.container = fctr;
20040         this.footer = Roo.factory(this.footer, Roo);
20041         fctr.insertFirst(this.el);
20042         
20043         // this is a bit insane - as the paging toolbar seems to detach the el..
20044 //        dom.parentNode.parentNode.parentNode
20045          // they get detached?
20046     }
20047     
20048     
20049     Roo.View.superclass.constructor.call(this);
20050     
20051     
20052 };
20053
20054 Roo.extend(Roo.View, Roo.util.Observable, {
20055     
20056      /**
20057      * @cfg {Roo.data.Store} store Data store to load data from.
20058      */
20059     store : false,
20060     
20061     /**
20062      * @cfg {String|Roo.Element} el The container element.
20063      */
20064     el : '',
20065     
20066     /**
20067      * @cfg {String|Roo.Template} tpl The template used by this View 
20068      */
20069     tpl : false,
20070     /**
20071      * @cfg {String} dataName the named area of the template to use as the data area
20072      *                          Works with domtemplates roo-name="name"
20073      */
20074     dataName: false,
20075     /**
20076      * @cfg {String} selectedClass The css class to add to selected nodes
20077      */
20078     selectedClass : "x-view-selected",
20079      /**
20080      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20081      */
20082     emptyText : "",
20083     
20084     /**
20085      * @cfg {String} text to display on mask (default Loading)
20086      */
20087     mask : false,
20088     /**
20089      * @cfg {Boolean} multiSelect Allow multiple selection
20090      */
20091     multiSelect : false,
20092     /**
20093      * @cfg {Boolean} singleSelect Allow single selection
20094      */
20095     singleSelect:  false,
20096     
20097     /**
20098      * @cfg {Boolean} toggleSelect - selecting 
20099      */
20100     toggleSelect : false,
20101     
20102     /**
20103      * @cfg {Boolean} tickable - selecting 
20104      */
20105     tickable : false,
20106     
20107     /**
20108      * Returns the element this view is bound to.
20109      * @return {Roo.Element}
20110      */
20111     getEl : function(){
20112         return this.wrapEl;
20113     },
20114     
20115     
20116
20117     /**
20118      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20119      */
20120     refresh : function(){
20121         //Roo.log('refresh');
20122         var t = this.tpl;
20123         
20124         // if we are using something like 'domtemplate', then
20125         // the what gets used is:
20126         // t.applySubtemplate(NAME, data, wrapping data..)
20127         // the outer template then get' applied with
20128         //     the store 'extra data'
20129         // and the body get's added to the
20130         //      roo-name="data" node?
20131         //      <span class='roo-tpl-{name}'></span> ?????
20132         
20133         
20134         
20135         this.clearSelections();
20136         this.el.update("");
20137         var html = [];
20138         var records = this.store.getRange();
20139         if(records.length < 1) {
20140             
20141             // is this valid??  = should it render a template??
20142             
20143             this.el.update(this.emptyText);
20144             return;
20145         }
20146         var el = this.el;
20147         if (this.dataName) {
20148             this.el.update(t.apply(this.store.meta)); //????
20149             el = this.el.child('.roo-tpl-' + this.dataName);
20150         }
20151         
20152         for(var i = 0, len = records.length; i < len; i++){
20153             var data = this.prepareData(records[i].data, i, records[i]);
20154             this.fireEvent("preparedata", this, data, i, records[i]);
20155             
20156             var d = Roo.apply({}, data);
20157             
20158             if(this.tickable){
20159                 Roo.apply(d, {'roo-id' : Roo.id()});
20160                 
20161                 var _this = this;
20162             
20163                 Roo.each(this.parent.item, function(item){
20164                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20165                         return;
20166                     }
20167                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20168                 });
20169             }
20170             
20171             html[html.length] = Roo.util.Format.trim(
20172                 this.dataName ?
20173                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20174                     t.apply(d)
20175             );
20176         }
20177         
20178         
20179         
20180         el.update(html.join(""));
20181         this.nodes = el.dom.childNodes;
20182         this.updateIndexes(0);
20183     },
20184     
20185
20186     /**
20187      * Function to override to reformat the data that is sent to
20188      * the template for each node.
20189      * DEPRICATED - use the preparedata event handler.
20190      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20191      * a JSON object for an UpdateManager bound view).
20192      */
20193     prepareData : function(data, index, record)
20194     {
20195         this.fireEvent("preparedata", this, data, index, record);
20196         return data;
20197     },
20198
20199     onUpdate : function(ds, record){
20200         // Roo.log('on update');   
20201         this.clearSelections();
20202         var index = this.store.indexOf(record);
20203         var n = this.nodes[index];
20204         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20205         n.parentNode.removeChild(n);
20206         this.updateIndexes(index, index);
20207     },
20208
20209     
20210     
20211 // --------- FIXME     
20212     onAdd : function(ds, records, index)
20213     {
20214         //Roo.log(['on Add', ds, records, index] );        
20215         this.clearSelections();
20216         if(this.nodes.length == 0){
20217             this.refresh();
20218             return;
20219         }
20220         var n = this.nodes[index];
20221         for(var i = 0, len = records.length; i < len; i++){
20222             var d = this.prepareData(records[i].data, i, records[i]);
20223             if(n){
20224                 this.tpl.insertBefore(n, d);
20225             }else{
20226                 
20227                 this.tpl.append(this.el, d);
20228             }
20229         }
20230         this.updateIndexes(index);
20231     },
20232
20233     onRemove : function(ds, record, index){
20234        // Roo.log('onRemove');
20235         this.clearSelections();
20236         var el = this.dataName  ?
20237             this.el.child('.roo-tpl-' + this.dataName) :
20238             this.el; 
20239         
20240         el.dom.removeChild(this.nodes[index]);
20241         this.updateIndexes(index);
20242     },
20243
20244     /**
20245      * Refresh an individual node.
20246      * @param {Number} index
20247      */
20248     refreshNode : function(index){
20249         this.onUpdate(this.store, this.store.getAt(index));
20250     },
20251
20252     updateIndexes : function(startIndex, endIndex){
20253         var ns = this.nodes;
20254         startIndex = startIndex || 0;
20255         endIndex = endIndex || ns.length - 1;
20256         for(var i = startIndex; i <= endIndex; i++){
20257             ns[i].nodeIndex = i;
20258         }
20259     },
20260
20261     /**
20262      * Changes the data store this view uses and refresh the view.
20263      * @param {Store} store
20264      */
20265     setStore : function(store, initial){
20266         if(!initial && this.store){
20267             this.store.un("datachanged", this.refresh);
20268             this.store.un("add", this.onAdd);
20269             this.store.un("remove", this.onRemove);
20270             this.store.un("update", this.onUpdate);
20271             this.store.un("clear", this.refresh);
20272             this.store.un("beforeload", this.onBeforeLoad);
20273             this.store.un("load", this.onLoad);
20274             this.store.un("loadexception", this.onLoad);
20275         }
20276         if(store){
20277           
20278             store.on("datachanged", this.refresh, this);
20279             store.on("add", this.onAdd, this);
20280             store.on("remove", this.onRemove, this);
20281             store.on("update", this.onUpdate, this);
20282             store.on("clear", this.refresh, this);
20283             store.on("beforeload", this.onBeforeLoad, this);
20284             store.on("load", this.onLoad, this);
20285             store.on("loadexception", this.onLoad, this);
20286         }
20287         
20288         if(store){
20289             this.refresh();
20290         }
20291     },
20292     /**
20293      * onbeforeLoad - masks the loading area.
20294      *
20295      */
20296     onBeforeLoad : function(store,opts)
20297     {
20298          //Roo.log('onBeforeLoad');   
20299         if (!opts.add) {
20300             this.el.update("");
20301         }
20302         this.el.mask(this.mask ? this.mask : "Loading" ); 
20303     },
20304     onLoad : function ()
20305     {
20306         this.el.unmask();
20307     },
20308     
20309
20310     /**
20311      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20312      * @param {HTMLElement} node
20313      * @return {HTMLElement} The template node
20314      */
20315     findItemFromChild : function(node){
20316         var el = this.dataName  ?
20317             this.el.child('.roo-tpl-' + this.dataName,true) :
20318             this.el.dom; 
20319         
20320         if(!node || node.parentNode == el){
20321                     return node;
20322             }
20323             var p = node.parentNode;
20324             while(p && p != el){
20325             if(p.parentNode == el){
20326                 return p;
20327             }
20328             p = p.parentNode;
20329         }
20330             return null;
20331     },
20332
20333     /** @ignore */
20334     onClick : function(e){
20335         var item = this.findItemFromChild(e.getTarget());
20336         if(item){
20337             var index = this.indexOf(item);
20338             if(this.onItemClick(item, index, e) !== false){
20339                 this.fireEvent("click", this, index, item, e);
20340             }
20341         }else{
20342             this.clearSelections();
20343         }
20344     },
20345
20346     /** @ignore */
20347     onContextMenu : function(e){
20348         var item = this.findItemFromChild(e.getTarget());
20349         if(item){
20350             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20351         }
20352     },
20353
20354     /** @ignore */
20355     onDblClick : function(e){
20356         var item = this.findItemFromChild(e.getTarget());
20357         if(item){
20358             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20359         }
20360     },
20361
20362     onItemClick : function(item, index, e)
20363     {
20364         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20365             return false;
20366         }
20367         if (this.toggleSelect) {
20368             var m = this.isSelected(item) ? 'unselect' : 'select';
20369             //Roo.log(m);
20370             var _t = this;
20371             _t[m](item, true, false);
20372             return true;
20373         }
20374         if(this.multiSelect || this.singleSelect){
20375             if(this.multiSelect && e.shiftKey && this.lastSelection){
20376                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20377             }else{
20378                 this.select(item, this.multiSelect && e.ctrlKey);
20379                 this.lastSelection = item;
20380             }
20381             
20382             if(!this.tickable){
20383                 e.preventDefault();
20384             }
20385             
20386         }
20387         return true;
20388     },
20389
20390     /**
20391      * Get the number of selected nodes.
20392      * @return {Number}
20393      */
20394     getSelectionCount : function(){
20395         return this.selections.length;
20396     },
20397
20398     /**
20399      * Get the currently selected nodes.
20400      * @return {Array} An array of HTMLElements
20401      */
20402     getSelectedNodes : function(){
20403         return this.selections;
20404     },
20405
20406     /**
20407      * Get the indexes of the selected nodes.
20408      * @return {Array}
20409      */
20410     getSelectedIndexes : function(){
20411         var indexes = [], s = this.selections;
20412         for(var i = 0, len = s.length; i < len; i++){
20413             indexes.push(s[i].nodeIndex);
20414         }
20415         return indexes;
20416     },
20417
20418     /**
20419      * Clear all selections
20420      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20421      */
20422     clearSelections : function(suppressEvent){
20423         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20424             this.cmp.elements = this.selections;
20425             this.cmp.removeClass(this.selectedClass);
20426             this.selections = [];
20427             if(!suppressEvent){
20428                 this.fireEvent("selectionchange", this, this.selections);
20429             }
20430         }
20431     },
20432
20433     /**
20434      * Returns true if the passed node is selected
20435      * @param {HTMLElement/Number} node The node or node index
20436      * @return {Boolean}
20437      */
20438     isSelected : function(node){
20439         var s = this.selections;
20440         if(s.length < 1){
20441             return false;
20442         }
20443         node = this.getNode(node);
20444         return s.indexOf(node) !== -1;
20445     },
20446
20447     /**
20448      * Selects nodes.
20449      * @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
20450      * @param {Boolean} keepExisting (optional) true to keep existing selections
20451      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20452      */
20453     select : function(nodeInfo, keepExisting, suppressEvent){
20454         if(nodeInfo instanceof Array){
20455             if(!keepExisting){
20456                 this.clearSelections(true);
20457             }
20458             for(var i = 0, len = nodeInfo.length; i < len; i++){
20459                 this.select(nodeInfo[i], true, true);
20460             }
20461             return;
20462         } 
20463         var node = this.getNode(nodeInfo);
20464         if(!node || this.isSelected(node)){
20465             return; // already selected.
20466         }
20467         if(!keepExisting){
20468             this.clearSelections(true);
20469         }
20470         
20471         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20472             Roo.fly(node).addClass(this.selectedClass);
20473             this.selections.push(node);
20474             if(!suppressEvent){
20475                 this.fireEvent("selectionchange", this, this.selections);
20476             }
20477         }
20478         
20479         
20480     },
20481       /**
20482      * Unselects nodes.
20483      * @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
20484      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20485      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20486      */
20487     unselect : function(nodeInfo, keepExisting, suppressEvent)
20488     {
20489         if(nodeInfo instanceof Array){
20490             Roo.each(this.selections, function(s) {
20491                 this.unselect(s, nodeInfo);
20492             }, this);
20493             return;
20494         }
20495         var node = this.getNode(nodeInfo);
20496         if(!node || !this.isSelected(node)){
20497             //Roo.log("not selected");
20498             return; // not selected.
20499         }
20500         // fireevent???
20501         var ns = [];
20502         Roo.each(this.selections, function(s) {
20503             if (s == node ) {
20504                 Roo.fly(node).removeClass(this.selectedClass);
20505
20506                 return;
20507             }
20508             ns.push(s);
20509         },this);
20510         
20511         this.selections= ns;
20512         this.fireEvent("selectionchange", this, this.selections);
20513     },
20514
20515     /**
20516      * Gets a template node.
20517      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20518      * @return {HTMLElement} The node or null if it wasn't found
20519      */
20520     getNode : function(nodeInfo){
20521         if(typeof nodeInfo == "string"){
20522             return document.getElementById(nodeInfo);
20523         }else if(typeof nodeInfo == "number"){
20524             return this.nodes[nodeInfo];
20525         }
20526         return nodeInfo;
20527     },
20528
20529     /**
20530      * Gets a range template nodes.
20531      * @param {Number} startIndex
20532      * @param {Number} endIndex
20533      * @return {Array} An array of nodes
20534      */
20535     getNodes : function(start, end){
20536         var ns = this.nodes;
20537         start = start || 0;
20538         end = typeof end == "undefined" ? ns.length - 1 : end;
20539         var nodes = [];
20540         if(start <= end){
20541             for(var i = start; i <= end; i++){
20542                 nodes.push(ns[i]);
20543             }
20544         } else{
20545             for(var i = start; i >= end; i--){
20546                 nodes.push(ns[i]);
20547             }
20548         }
20549         return nodes;
20550     },
20551
20552     /**
20553      * Finds the index of the passed node
20554      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20555      * @return {Number} The index of the node or -1
20556      */
20557     indexOf : function(node){
20558         node = this.getNode(node);
20559         if(typeof node.nodeIndex == "number"){
20560             return node.nodeIndex;
20561         }
20562         var ns = this.nodes;
20563         for(var i = 0, len = ns.length; i < len; i++){
20564             if(ns[i] == node){
20565                 return i;
20566             }
20567         }
20568         return -1;
20569     }
20570 });
20571 /*
20572  * - LGPL
20573  *
20574  * based on jquery fullcalendar
20575  * 
20576  */
20577
20578 Roo.bootstrap = Roo.bootstrap || {};
20579 /**
20580  * @class Roo.bootstrap.Calendar
20581  * @extends Roo.bootstrap.Component
20582  * Bootstrap Calendar class
20583  * @cfg {Boolean} loadMask (true|false) default false
20584  * @cfg {Object} header generate the user specific header of the calendar, default false
20585
20586  * @constructor
20587  * Create a new Container
20588  * @param {Object} config The config object
20589  */
20590
20591
20592
20593 Roo.bootstrap.Calendar = function(config){
20594     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20595      this.addEvents({
20596         /**
20597              * @event select
20598              * Fires when a date is selected
20599              * @param {DatePicker} this
20600              * @param {Date} date The selected date
20601              */
20602         'select': true,
20603         /**
20604              * @event monthchange
20605              * Fires when the displayed month changes 
20606              * @param {DatePicker} this
20607              * @param {Date} date The selected month
20608              */
20609         'monthchange': true,
20610         /**
20611              * @event evententer
20612              * Fires when mouse over an event
20613              * @param {Calendar} this
20614              * @param {event} Event
20615              */
20616         'evententer': true,
20617         /**
20618              * @event eventleave
20619              * Fires when the mouse leaves an
20620              * @param {Calendar} this
20621              * @param {event}
20622              */
20623         'eventleave': true,
20624         /**
20625              * @event eventclick
20626              * Fires when the mouse click an
20627              * @param {Calendar} this
20628              * @param {event}
20629              */
20630         'eventclick': true
20631         
20632     });
20633
20634 };
20635
20636 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20637     
20638           /**
20639      * @cfg {Roo.data.Store} store
20640      * The data source for the calendar
20641      */
20642         store : false,
20643      /**
20644      * @cfg {Number} startDay
20645      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20646      */
20647     startDay : 0,
20648     
20649     loadMask : false,
20650     
20651     header : false,
20652       
20653     getAutoCreate : function(){
20654         
20655         
20656         var fc_button = function(name, corner, style, content ) {
20657             return Roo.apply({},{
20658                 tag : 'span',
20659                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20660                          (corner.length ?
20661                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20662                             ''
20663                         ),
20664                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20665                 unselectable: 'on'
20666             });
20667         };
20668         
20669         var header = {};
20670         
20671         if(!this.header){
20672             header = {
20673                 tag : 'table',
20674                 cls : 'fc-header',
20675                 style : 'width:100%',
20676                 cn : [
20677                     {
20678                         tag: 'tr',
20679                         cn : [
20680                             {
20681                                 tag : 'td',
20682                                 cls : 'fc-header-left',
20683                                 cn : [
20684                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20685                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20686                                     { tag: 'span', cls: 'fc-header-space' },
20687                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20688
20689
20690                                 ]
20691                             },
20692
20693                             {
20694                                 tag : 'td',
20695                                 cls : 'fc-header-center',
20696                                 cn : [
20697                                     {
20698                                         tag: 'span',
20699                                         cls: 'fc-header-title',
20700                                         cn : {
20701                                             tag: 'H2',
20702                                             html : 'month / year'
20703                                         }
20704                                     }
20705
20706                                 ]
20707                             },
20708                             {
20709                                 tag : 'td',
20710                                 cls : 'fc-header-right',
20711                                 cn : [
20712                               /*      fc_button('month', 'left', '', 'month' ),
20713                                     fc_button('week', '', '', 'week' ),
20714                                     fc_button('day', 'right', '', 'day' )
20715                                 */    
20716
20717                                 ]
20718                             }
20719
20720                         ]
20721                     }
20722                 ]
20723             };
20724         }
20725         
20726         header = this.header;
20727         
20728        
20729         var cal_heads = function() {
20730             var ret = [];
20731             // fixme - handle this.
20732             
20733             for (var i =0; i < Date.dayNames.length; i++) {
20734                 var d = Date.dayNames[i];
20735                 ret.push({
20736                     tag: 'th',
20737                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20738                     html : d.substring(0,3)
20739                 });
20740                 
20741             }
20742             ret[0].cls += ' fc-first';
20743             ret[6].cls += ' fc-last';
20744             return ret;
20745         };
20746         var cal_cell = function(n) {
20747             return  {
20748                 tag: 'td',
20749                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20750                 cn : [
20751                     {
20752                         cn : [
20753                             {
20754                                 cls: 'fc-day-number',
20755                                 html: 'D'
20756                             },
20757                             {
20758                                 cls: 'fc-day-content',
20759                              
20760                                 cn : [
20761                                      {
20762                                         style: 'position: relative;' // height: 17px;
20763                                     }
20764                                 ]
20765                             }
20766                             
20767                             
20768                         ]
20769                     }
20770                 ]
20771                 
20772             }
20773         };
20774         var cal_rows = function() {
20775             
20776             var ret = [];
20777             for (var r = 0; r < 6; r++) {
20778                 var row= {
20779                     tag : 'tr',
20780                     cls : 'fc-week',
20781                     cn : []
20782                 };
20783                 
20784                 for (var i =0; i < Date.dayNames.length; i++) {
20785                     var d = Date.dayNames[i];
20786                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20787
20788                 }
20789                 row.cn[0].cls+=' fc-first';
20790                 row.cn[0].cn[0].style = 'min-height:90px';
20791                 row.cn[6].cls+=' fc-last';
20792                 ret.push(row);
20793                 
20794             }
20795             ret[0].cls += ' fc-first';
20796             ret[4].cls += ' fc-prev-last';
20797             ret[5].cls += ' fc-last';
20798             return ret;
20799             
20800         };
20801         
20802         var cal_table = {
20803             tag: 'table',
20804             cls: 'fc-border-separate',
20805             style : 'width:100%',
20806             cellspacing  : 0,
20807             cn : [
20808                 { 
20809                     tag: 'thead',
20810                     cn : [
20811                         { 
20812                             tag: 'tr',
20813                             cls : 'fc-first fc-last',
20814                             cn : cal_heads()
20815                         }
20816                     ]
20817                 },
20818                 { 
20819                     tag: 'tbody',
20820                     cn : cal_rows()
20821                 }
20822                   
20823             ]
20824         };
20825          
20826          var cfg = {
20827             cls : 'fc fc-ltr',
20828             cn : [
20829                 header,
20830                 {
20831                     cls : 'fc-content',
20832                     style : "position: relative;",
20833                     cn : [
20834                         {
20835                             cls : 'fc-view fc-view-month fc-grid',
20836                             style : 'position: relative',
20837                             unselectable : 'on',
20838                             cn : [
20839                                 {
20840                                     cls : 'fc-event-container',
20841                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20842                                 },
20843                                 cal_table
20844                             ]
20845                         }
20846                     ]
20847     
20848                 }
20849            ] 
20850             
20851         };
20852         
20853          
20854         
20855         return cfg;
20856     },
20857     
20858     
20859     initEvents : function()
20860     {
20861         if(!this.store){
20862             throw "can not find store for calendar";
20863         }
20864         
20865         var mark = {
20866             tag: "div",
20867             cls:"x-dlg-mask",
20868             style: "text-align:center",
20869             cn: [
20870                 {
20871                     tag: "div",
20872                     style: "background-color:white;width:50%;margin:250 auto",
20873                     cn: [
20874                         {
20875                             tag: "img",
20876                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20877                         },
20878                         {
20879                             tag: "span",
20880                             html: "Loading"
20881                         }
20882                         
20883                     ]
20884                 }
20885             ]
20886         };
20887         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20888         
20889         var size = this.el.select('.fc-content', true).first().getSize();
20890         this.maskEl.setSize(size.width, size.height);
20891         this.maskEl.enableDisplayMode("block");
20892         if(!this.loadMask){
20893             this.maskEl.hide();
20894         }
20895         
20896         this.store = Roo.factory(this.store, Roo.data);
20897         this.store.on('load', this.onLoad, this);
20898         this.store.on('beforeload', this.onBeforeLoad, this);
20899         
20900         this.resize();
20901         
20902         this.cells = this.el.select('.fc-day',true);
20903         //Roo.log(this.cells);
20904         this.textNodes = this.el.query('.fc-day-number');
20905         this.cells.addClassOnOver('fc-state-hover');
20906         
20907         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20908         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20909         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20910         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20911         
20912         this.on('monthchange', this.onMonthChange, this);
20913         
20914         this.update(new Date().clearTime());
20915     },
20916     
20917     resize : function() {
20918         var sz  = this.el.getSize();
20919         
20920         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20921         this.el.select('.fc-day-content div',true).setHeight(34);
20922     },
20923     
20924     
20925     // private
20926     showPrevMonth : function(e){
20927         this.update(this.activeDate.add("mo", -1));
20928     },
20929     showToday : function(e){
20930         this.update(new Date().clearTime());
20931     },
20932     // private
20933     showNextMonth : function(e){
20934         this.update(this.activeDate.add("mo", 1));
20935     },
20936
20937     // private
20938     showPrevYear : function(){
20939         this.update(this.activeDate.add("y", -1));
20940     },
20941
20942     // private
20943     showNextYear : function(){
20944         this.update(this.activeDate.add("y", 1));
20945     },
20946
20947     
20948    // private
20949     update : function(date)
20950     {
20951         var vd = this.activeDate;
20952         this.activeDate = date;
20953 //        if(vd && this.el){
20954 //            var t = date.getTime();
20955 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20956 //                Roo.log('using add remove');
20957 //                
20958 //                this.fireEvent('monthchange', this, date);
20959 //                
20960 //                this.cells.removeClass("fc-state-highlight");
20961 //                this.cells.each(function(c){
20962 //                   if(c.dateValue == t){
20963 //                       c.addClass("fc-state-highlight");
20964 //                       setTimeout(function(){
20965 //                            try{c.dom.firstChild.focus();}catch(e){}
20966 //                       }, 50);
20967 //                       return false;
20968 //                   }
20969 //                   return true;
20970 //                });
20971 //                return;
20972 //            }
20973 //        }
20974         
20975         var days = date.getDaysInMonth();
20976         
20977         var firstOfMonth = date.getFirstDateOfMonth();
20978         var startingPos = firstOfMonth.getDay()-this.startDay;
20979         
20980         if(startingPos < this.startDay){
20981             startingPos += 7;
20982         }
20983         
20984         var pm = date.add(Date.MONTH, -1);
20985         var prevStart = pm.getDaysInMonth()-startingPos;
20986 //        
20987         this.cells = this.el.select('.fc-day',true);
20988         this.textNodes = this.el.query('.fc-day-number');
20989         this.cells.addClassOnOver('fc-state-hover');
20990         
20991         var cells = this.cells.elements;
20992         var textEls = this.textNodes;
20993         
20994         Roo.each(cells, function(cell){
20995             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20996         });
20997         
20998         days += startingPos;
20999
21000         // convert everything to numbers so it's fast
21001         var day = 86400000;
21002         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21003         //Roo.log(d);
21004         //Roo.log(pm);
21005         //Roo.log(prevStart);
21006         
21007         var today = new Date().clearTime().getTime();
21008         var sel = date.clearTime().getTime();
21009         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21010         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21011         var ddMatch = this.disabledDatesRE;
21012         var ddText = this.disabledDatesText;
21013         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21014         var ddaysText = this.disabledDaysText;
21015         var format = this.format;
21016         
21017         var setCellClass = function(cal, cell){
21018             cell.row = 0;
21019             cell.events = [];
21020             cell.more = [];
21021             //Roo.log('set Cell Class');
21022             cell.title = "";
21023             var t = d.getTime();
21024             
21025             //Roo.log(d);
21026             
21027             cell.dateValue = t;
21028             if(t == today){
21029                 cell.className += " fc-today";
21030                 cell.className += " fc-state-highlight";
21031                 cell.title = cal.todayText;
21032             }
21033             if(t == sel){
21034                 // disable highlight in other month..
21035                 //cell.className += " fc-state-highlight";
21036                 
21037             }
21038             // disabling
21039             if(t < min) {
21040                 cell.className = " fc-state-disabled";
21041                 cell.title = cal.minText;
21042                 return;
21043             }
21044             if(t > max) {
21045                 cell.className = " fc-state-disabled";
21046                 cell.title = cal.maxText;
21047                 return;
21048             }
21049             if(ddays){
21050                 if(ddays.indexOf(d.getDay()) != -1){
21051                     cell.title = ddaysText;
21052                     cell.className = " fc-state-disabled";
21053                 }
21054             }
21055             if(ddMatch && format){
21056                 var fvalue = d.dateFormat(format);
21057                 if(ddMatch.test(fvalue)){
21058                     cell.title = ddText.replace("%0", fvalue);
21059                     cell.className = " fc-state-disabled";
21060                 }
21061             }
21062             
21063             if (!cell.initialClassName) {
21064                 cell.initialClassName = cell.dom.className;
21065             }
21066             
21067             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21068         };
21069
21070         var i = 0;
21071         
21072         for(; i < startingPos; i++) {
21073             textEls[i].innerHTML = (++prevStart);
21074             d.setDate(d.getDate()+1);
21075             
21076             cells[i].className = "fc-past fc-other-month";
21077             setCellClass(this, cells[i]);
21078         }
21079         
21080         var intDay = 0;
21081         
21082         for(; i < days; i++){
21083             intDay = i - startingPos + 1;
21084             textEls[i].innerHTML = (intDay);
21085             d.setDate(d.getDate()+1);
21086             
21087             cells[i].className = ''; // "x-date-active";
21088             setCellClass(this, cells[i]);
21089         }
21090         var extraDays = 0;
21091         
21092         for(; i < 42; i++) {
21093             textEls[i].innerHTML = (++extraDays);
21094             d.setDate(d.getDate()+1);
21095             
21096             cells[i].className = "fc-future fc-other-month";
21097             setCellClass(this, cells[i]);
21098         }
21099         
21100         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21101         
21102         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21103         
21104         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21105         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21106         
21107         if(totalRows != 6){
21108             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21109             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21110         }
21111         
21112         this.fireEvent('monthchange', this, date);
21113         
21114         
21115         /*
21116         if(!this.internalRender){
21117             var main = this.el.dom.firstChild;
21118             var w = main.offsetWidth;
21119             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21120             Roo.fly(main).setWidth(w);
21121             this.internalRender = true;
21122             // opera does not respect the auto grow header center column
21123             // then, after it gets a width opera refuses to recalculate
21124             // without a second pass
21125             if(Roo.isOpera && !this.secondPass){
21126                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21127                 this.secondPass = true;
21128                 this.update.defer(10, this, [date]);
21129             }
21130         }
21131         */
21132         
21133     },
21134     
21135     findCell : function(dt) {
21136         dt = dt.clearTime().getTime();
21137         var ret = false;
21138         this.cells.each(function(c){
21139             //Roo.log("check " +c.dateValue + '?=' + dt);
21140             if(c.dateValue == dt){
21141                 ret = c;
21142                 return false;
21143             }
21144             return true;
21145         });
21146         
21147         return ret;
21148     },
21149     
21150     findCells : function(ev) {
21151         var s = ev.start.clone().clearTime().getTime();
21152        // Roo.log(s);
21153         var e= ev.end.clone().clearTime().getTime();
21154        // Roo.log(e);
21155         var ret = [];
21156         this.cells.each(function(c){
21157              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21158             
21159             if(c.dateValue > e){
21160                 return ;
21161             }
21162             if(c.dateValue < s){
21163                 return ;
21164             }
21165             ret.push(c);
21166         });
21167         
21168         return ret;    
21169     },
21170     
21171 //    findBestRow: function(cells)
21172 //    {
21173 //        var ret = 0;
21174 //        
21175 //        for (var i =0 ; i < cells.length;i++) {
21176 //            ret  = Math.max(cells[i].rows || 0,ret);
21177 //        }
21178 //        return ret;
21179 //        
21180 //    },
21181     
21182     
21183     addItem : function(ev)
21184     {
21185         // look for vertical location slot in
21186         var cells = this.findCells(ev);
21187         
21188 //        ev.row = this.findBestRow(cells);
21189         
21190         // work out the location.
21191         
21192         var crow = false;
21193         var rows = [];
21194         for(var i =0; i < cells.length; i++) {
21195             
21196             cells[i].row = cells[0].row;
21197             
21198             if(i == 0){
21199                 cells[i].row = cells[i].row + 1;
21200             }
21201             
21202             if (!crow) {
21203                 crow = {
21204                     start : cells[i],
21205                     end :  cells[i]
21206                 };
21207                 continue;
21208             }
21209             if (crow.start.getY() == cells[i].getY()) {
21210                 // on same row.
21211                 crow.end = cells[i];
21212                 continue;
21213             }
21214             // different row.
21215             rows.push(crow);
21216             crow = {
21217                 start: cells[i],
21218                 end : cells[i]
21219             };
21220             
21221         }
21222         
21223         rows.push(crow);
21224         ev.els = [];
21225         ev.rows = rows;
21226         ev.cells = cells;
21227         
21228         cells[0].events.push(ev);
21229         
21230         this.calevents.push(ev);
21231     },
21232     
21233     clearEvents: function() {
21234         
21235         if(!this.calevents){
21236             return;
21237         }
21238         
21239         Roo.each(this.cells.elements, function(c){
21240             c.row = 0;
21241             c.events = [];
21242             c.more = [];
21243         });
21244         
21245         Roo.each(this.calevents, function(e) {
21246             Roo.each(e.els, function(el) {
21247                 el.un('mouseenter' ,this.onEventEnter, this);
21248                 el.un('mouseleave' ,this.onEventLeave, this);
21249                 el.remove();
21250             },this);
21251         },this);
21252         
21253         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21254             e.remove();
21255         });
21256         
21257     },
21258     
21259     renderEvents: function()
21260     {   
21261         var _this = this;
21262         
21263         this.cells.each(function(c) {
21264             
21265             if(c.row < 5){
21266                 return;
21267             }
21268             
21269             var ev = c.events;
21270             
21271             var r = 4;
21272             if(c.row != c.events.length){
21273                 r = 4 - (4 - (c.row - c.events.length));
21274             }
21275             
21276             c.events = ev.slice(0, r);
21277             c.more = ev.slice(r);
21278             
21279             if(c.more.length && c.more.length == 1){
21280                 c.events.push(c.more.pop());
21281             }
21282             
21283             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21284             
21285         });
21286             
21287         this.cells.each(function(c) {
21288             
21289             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21290             
21291             
21292             for (var e = 0; e < c.events.length; e++){
21293                 var ev = c.events[e];
21294                 var rows = ev.rows;
21295                 
21296                 for(var i = 0; i < rows.length; i++) {
21297                 
21298                     // how many rows should it span..
21299
21300                     var  cfg = {
21301                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21302                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21303
21304                         unselectable : "on",
21305                         cn : [
21306                             {
21307                                 cls: 'fc-event-inner',
21308                                 cn : [
21309     //                                {
21310     //                                  tag:'span',
21311     //                                  cls: 'fc-event-time',
21312     //                                  html : cells.length > 1 ? '' : ev.time
21313     //                                },
21314                                     {
21315                                       tag:'span',
21316                                       cls: 'fc-event-title',
21317                                       html : String.format('{0}', ev.title)
21318                                     }
21319
21320
21321                                 ]
21322                             },
21323                             {
21324                                 cls: 'ui-resizable-handle ui-resizable-e',
21325                                 html : '&nbsp;&nbsp;&nbsp'
21326                             }
21327
21328                         ]
21329                     };
21330
21331                     if (i == 0) {
21332                         cfg.cls += ' fc-event-start';
21333                     }
21334                     if ((i+1) == rows.length) {
21335                         cfg.cls += ' fc-event-end';
21336                     }
21337
21338                     var ctr = _this.el.select('.fc-event-container',true).first();
21339                     var cg = ctr.createChild(cfg);
21340
21341                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21342                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21343
21344                     var r = (c.more.length) ? 1 : 0;
21345                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21346                     cg.setWidth(ebox.right - sbox.x -2);
21347
21348                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21349                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21350                     cg.on('click', _this.onEventClick, _this, ev);
21351
21352                     ev.els.push(cg);
21353                     
21354                 }
21355                 
21356             }
21357             
21358             
21359             if(c.more.length){
21360                 var  cfg = {
21361                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21362                     style : 'position: absolute',
21363                     unselectable : "on",
21364                     cn : [
21365                         {
21366                             cls: 'fc-event-inner',
21367                             cn : [
21368                                 {
21369                                   tag:'span',
21370                                   cls: 'fc-event-title',
21371                                   html : 'More'
21372                                 }
21373
21374
21375                             ]
21376                         },
21377                         {
21378                             cls: 'ui-resizable-handle ui-resizable-e',
21379                             html : '&nbsp;&nbsp;&nbsp'
21380                         }
21381
21382                     ]
21383                 };
21384
21385                 var ctr = _this.el.select('.fc-event-container',true).first();
21386                 var cg = ctr.createChild(cfg);
21387
21388                 var sbox = c.select('.fc-day-content',true).first().getBox();
21389                 var ebox = c.select('.fc-day-content',true).first().getBox();
21390                 //Roo.log(cg);
21391                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21392                 cg.setWidth(ebox.right - sbox.x -2);
21393
21394                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21395                 
21396             }
21397             
21398         });
21399         
21400         
21401         
21402     },
21403     
21404     onEventEnter: function (e, el,event,d) {
21405         this.fireEvent('evententer', this, el, event);
21406     },
21407     
21408     onEventLeave: function (e, el,event,d) {
21409         this.fireEvent('eventleave', this, el, event);
21410     },
21411     
21412     onEventClick: function (e, el,event,d) {
21413         this.fireEvent('eventclick', this, el, event);
21414     },
21415     
21416     onMonthChange: function () {
21417         this.store.load();
21418     },
21419     
21420     onMoreEventClick: function(e, el, more)
21421     {
21422         var _this = this;
21423         
21424         this.calpopover.placement = 'right';
21425         this.calpopover.setTitle('More');
21426         
21427         this.calpopover.setContent('');
21428         
21429         var ctr = this.calpopover.el.select('.popover-content', true).first();
21430         
21431         Roo.each(more, function(m){
21432             var cfg = {
21433                 cls : 'fc-event-hori fc-event-draggable',
21434                 html : m.title
21435             };
21436             var cg = ctr.createChild(cfg);
21437             
21438             cg.on('click', _this.onEventClick, _this, m);
21439         });
21440         
21441         this.calpopover.show(el);
21442         
21443         
21444     },
21445     
21446     onLoad: function () 
21447     {   
21448         this.calevents = [];
21449         var cal = this;
21450         
21451         if(this.store.getCount() > 0){
21452             this.store.data.each(function(d){
21453                cal.addItem({
21454                     id : d.data.id,
21455                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21456                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21457                     time : d.data.start_time,
21458                     title : d.data.title,
21459                     description : d.data.description,
21460                     venue : d.data.venue
21461                 });
21462             });
21463         }
21464         
21465         this.renderEvents();
21466         
21467         if(this.calevents.length && this.loadMask){
21468             this.maskEl.hide();
21469         }
21470     },
21471     
21472     onBeforeLoad: function()
21473     {
21474         this.clearEvents();
21475         if(this.loadMask){
21476             this.maskEl.show();
21477         }
21478     }
21479 });
21480
21481  
21482  /*
21483  * - LGPL
21484  *
21485  * element
21486  * 
21487  */
21488
21489 /**
21490  * @class Roo.bootstrap.Popover
21491  * @extends Roo.bootstrap.Component
21492  * @builder-top
21493  * @parent none
21494  * @children Roo.bootstrap.Component
21495  * Bootstrap Popover class
21496  * @cfg {String} html contents of the popover   (or false to use children..)
21497  * @cfg {String} title of popover (or false to hide)
21498  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21499  * @cfg {String} trigger click || hover (or false to trigger manually)
21500  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21501  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21502  *      - if false and it has a 'parent' then it will be automatically added to that element
21503  *      - if string - Roo.get  will be called 
21504  * @cfg {Number} delay - delay before showing
21505  
21506  * @constructor
21507  * Create a new Popover
21508  * @param {Object} config The config object
21509  */
21510
21511 Roo.bootstrap.Popover = function(config){
21512     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21513     
21514     this.addEvents({
21515         // raw events
21516          /**
21517          * @event show
21518          * After the popover show
21519          * 
21520          * @param {Roo.bootstrap.Popover} this
21521          */
21522         "show" : true,
21523         /**
21524          * @event hide
21525          * After the popover hide
21526          * 
21527          * @param {Roo.bootstrap.Popover} this
21528          */
21529         "hide" : true
21530     });
21531 };
21532
21533 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21534     
21535     title: false,
21536     html: false,
21537     
21538     placement : 'right',
21539     trigger : 'hover', // hover
21540     modal : false,
21541     delay : 0,
21542     
21543     over: false,
21544     
21545     can_build_overlaid : false,
21546     
21547     maskEl : false, // the mask element
21548     headerEl : false,
21549     contentEl : false,
21550     alignEl : false, // when show is called with an element - this get's stored.
21551     
21552     getChildContainer : function()
21553     {
21554         return this.contentEl;
21555         
21556     },
21557     getPopoverHeader : function()
21558     {
21559         this.title = true; // flag not to hide it..
21560         this.headerEl.addClass('p-0');
21561         return this.headerEl
21562     },
21563     
21564     
21565     getAutoCreate : function(){
21566          
21567         var cfg = {
21568            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21569            style: 'display:block',
21570            cn : [
21571                 {
21572                     cls : 'arrow'
21573                 },
21574                 {
21575                     cls : 'popover-inner ',
21576                     cn : [
21577                         {
21578                             tag: 'h3',
21579                             cls: 'popover-title popover-header',
21580                             html : this.title === false ? '' : this.title
21581                         },
21582                         {
21583                             cls : 'popover-content popover-body '  + (this.cls || ''),
21584                             html : this.html || ''
21585                         }
21586                     ]
21587                     
21588                 }
21589            ]
21590         };
21591         
21592         return cfg;
21593     },
21594     /**
21595      * @param {string} the title
21596      */
21597     setTitle: function(str)
21598     {
21599         this.title = str;
21600         if (this.el) {
21601             this.headerEl.dom.innerHTML = str;
21602         }
21603         
21604     },
21605     /**
21606      * @param {string} the body content
21607      */
21608     setContent: function(str)
21609     {
21610         this.html = str;
21611         if (this.contentEl) {
21612             this.contentEl.dom.innerHTML = str;
21613         }
21614         
21615     },
21616     // as it get's added to the bottom of the page.
21617     onRender : function(ct, position)
21618     {
21619         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21620         
21621         
21622         
21623         if(!this.el){
21624             var cfg = Roo.apply({},  this.getAutoCreate());
21625             cfg.id = Roo.id();
21626             
21627             if (this.cls) {
21628                 cfg.cls += ' ' + this.cls;
21629             }
21630             if (this.style) {
21631                 cfg.style = this.style;
21632             }
21633             //Roo.log("adding to ");
21634             this.el = Roo.get(document.body).createChild(cfg, position);
21635 //            Roo.log(this.el);
21636         }
21637         
21638         this.contentEl = this.el.select('.popover-content',true).first();
21639         this.headerEl =  this.el.select('.popover-title',true).first();
21640         
21641         var nitems = [];
21642         if(typeof(this.items) != 'undefined'){
21643             var items = this.items;
21644             delete this.items;
21645
21646             for(var i =0;i < items.length;i++) {
21647                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21648             }
21649         }
21650
21651         this.items = nitems;
21652         
21653         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21654         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21655         
21656         
21657         
21658         this.initEvents();
21659     },
21660     
21661     resizeMask : function()
21662     {
21663         this.maskEl.setSize(
21664             Roo.lib.Dom.getViewWidth(true),
21665             Roo.lib.Dom.getViewHeight(true)
21666         );
21667     },
21668     
21669     initEvents : function()
21670     {
21671         
21672         if (!this.modal) { 
21673             Roo.bootstrap.Popover.register(this);
21674         }
21675          
21676         this.arrowEl = this.el.select('.arrow',true).first();
21677         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21678         this.el.enableDisplayMode('block');
21679         this.el.hide();
21680  
21681         
21682         if (this.over === false && !this.parent()) {
21683             return; 
21684         }
21685         if (this.triggers === false) {
21686             return;
21687         }
21688          
21689         // support parent
21690         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21691         var triggers = this.trigger ? this.trigger.split(' ') : [];
21692         Roo.each(triggers, function(trigger) {
21693         
21694             if (trigger == 'click') {
21695                 on_el.on('click', this.toggle, this);
21696             } else if (trigger != 'manual') {
21697                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21698                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21699       
21700                 on_el.on(eventIn  ,this.enter, this);
21701                 on_el.on(eventOut, this.leave, this);
21702             }
21703         }, this);
21704     },
21705     
21706     
21707     // private
21708     timeout : null,
21709     hoverState : null,
21710     
21711     toggle : function () {
21712         this.hoverState == 'in' ? this.leave() : this.enter();
21713     },
21714     
21715     enter : function () {
21716         
21717         clearTimeout(this.timeout);
21718     
21719         this.hoverState = 'in';
21720     
21721         if (!this.delay || !this.delay.show) {
21722             this.show();
21723             return;
21724         }
21725         var _t = this;
21726         this.timeout = setTimeout(function () {
21727             if (_t.hoverState == 'in') {
21728                 _t.show();
21729             }
21730         }, this.delay.show)
21731     },
21732     
21733     leave : function() {
21734         clearTimeout(this.timeout);
21735     
21736         this.hoverState = 'out';
21737     
21738         if (!this.delay || !this.delay.hide) {
21739             this.hide();
21740             return;
21741         }
21742         var _t = this;
21743         this.timeout = setTimeout(function () {
21744             if (_t.hoverState == 'out') {
21745                 _t.hide();
21746             }
21747         }, this.delay.hide)
21748     },
21749     /**
21750      * Show the popover
21751      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21752      * @param {string} (left|right|top|bottom) position
21753      */
21754     show : function (on_el, placement)
21755     {
21756         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21757         on_el = on_el || false; // default to false
21758          
21759         if (!on_el) {
21760             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21761                 on_el = this.parent().el;
21762             } else if (this.over) {
21763                 on_el = Roo.get(this.over);
21764             }
21765             
21766         }
21767         
21768         this.alignEl = Roo.get( on_el );
21769
21770         if (!this.el) {
21771             this.render(document.body);
21772         }
21773         
21774         
21775          
21776         
21777         if (this.title === false) {
21778             this.headerEl.hide();
21779         }
21780         
21781        
21782         this.el.show();
21783         this.el.dom.style.display = 'block';
21784          
21785  
21786         if (this.alignEl) {
21787             this.updatePosition(this.placement, true);
21788              
21789         } else {
21790             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21791             var es = this.el.getSize();
21792             var x = Roo.lib.Dom.getViewWidth()/2;
21793             var y = Roo.lib.Dom.getViewHeight()/2;
21794             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21795             
21796         }
21797
21798         
21799         //var arrow = this.el.select('.arrow',true).first();
21800         //arrow.set(align[2], 
21801         
21802         this.el.addClass('in');
21803         
21804          
21805         
21806         this.hoverState = 'in';
21807         
21808         if (this.modal) {
21809             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21810             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21811             this.maskEl.dom.style.display = 'block';
21812             this.maskEl.addClass('show');
21813         }
21814         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21815  
21816         this.fireEvent('show', this);
21817         
21818     },
21819     /**
21820      * fire this manually after loading a grid in the table for example
21821      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21822      * @param {Boolean} try and move it if we cant get right position.
21823      */
21824     updatePosition : function(placement, try_move)
21825     {
21826         // allow for calling with no parameters
21827         placement = placement   ? placement :  this.placement;
21828         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21829         
21830         this.el.removeClass([
21831             'fade','top','bottom', 'left', 'right','in',
21832             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21833         ]);
21834         this.el.addClass(placement + ' bs-popover-' + placement);
21835         
21836         if (!this.alignEl ) {
21837             return false;
21838         }
21839         
21840         switch (placement) {
21841             case 'right':
21842                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21843                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21844                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21845                     //normal display... or moved up/down.
21846                     this.el.setXY(offset);
21847                     var xy = this.alignEl.getAnchorXY('tr', false);
21848                     xy[0]+=2;xy[1]+=5;
21849                     this.arrowEl.setXY(xy);
21850                     return true;
21851                 }
21852                 // continue through...
21853                 return this.updatePosition('left', false);
21854                 
21855             
21856             case 'left':
21857                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21858                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21859                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21860                     //normal display... or moved up/down.
21861                     this.el.setXY(offset);
21862                     var xy = this.alignEl.getAnchorXY('tl', false);
21863                     xy[0]-=10;xy[1]+=5; // << fix me
21864                     this.arrowEl.setXY(xy);
21865                     return true;
21866                 }
21867                 // call self...
21868                 return this.updatePosition('right', false);
21869             
21870             case 'top':
21871                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21872                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21873                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21874                     //normal display... or moved up/down.
21875                     this.el.setXY(offset);
21876                     var xy = this.alignEl.getAnchorXY('t', false);
21877                     xy[1]-=10; // << fix me
21878                     this.arrowEl.setXY(xy);
21879                     return true;
21880                 }
21881                 // fall through
21882                return this.updatePosition('bottom', false);
21883             
21884             case 'bottom':
21885                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21886                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21887                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21888                     //normal display... or moved up/down.
21889                     this.el.setXY(offset);
21890                     var xy = this.alignEl.getAnchorXY('b', false);
21891                      xy[1]+=2; // << fix me
21892                     this.arrowEl.setXY(xy);
21893                     return true;
21894                 }
21895                 // fall through
21896                 return this.updatePosition('top', false);
21897                 
21898             
21899         }
21900         
21901         
21902         return false;
21903     },
21904     
21905     hide : function()
21906     {
21907         this.el.setXY([0,0]);
21908         this.el.removeClass('in');
21909         this.el.hide();
21910         this.hoverState = null;
21911         this.maskEl.hide(); // always..
21912         this.fireEvent('hide', this);
21913     }
21914     
21915 });
21916
21917
21918 Roo.apply(Roo.bootstrap.Popover, {
21919
21920     alignment : {
21921         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21922         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21923         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21924         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21925     },
21926     
21927     zIndex : 20001,
21928
21929     clickHander : false,
21930     
21931     
21932
21933     onMouseDown : function(e)
21934     {
21935         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21936             /// what is nothing is showing..
21937             this.hideAll();
21938         }
21939          
21940     },
21941     
21942     
21943     popups : [],
21944     
21945     register : function(popup)
21946     {
21947         if (!Roo.bootstrap.Popover.clickHandler) {
21948             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21949         }
21950         // hide other popups.
21951         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21952         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21953         this.hideAll(); //<< why?
21954         //this.popups.push(popup);
21955     },
21956     hideAll : function()
21957     {
21958         this.popups.forEach(function(p) {
21959             p.hide();
21960         });
21961     },
21962     onShow : function() {
21963         Roo.bootstrap.Popover.popups.push(this);
21964     },
21965     onHide : function() {
21966         Roo.bootstrap.Popover.popups.remove(this);
21967     } 
21968
21969 });
21970 /**
21971  * @class Roo.bootstrap.PopoverNav
21972  * @extends Roo.bootstrap.nav.Simplebar
21973  * @parent Roo.bootstrap.Popover
21974  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
21975  * @licence LGPL
21976  * Bootstrap Popover header navigation class
21977  * FIXME? should this go under nav?
21978  *
21979  * 
21980  * @constructor
21981  * Create a new Popover Header Navigation 
21982  * @param {Object} config The config object
21983  */
21984
21985 Roo.bootstrap.PopoverNav = function(config){
21986     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21987 };
21988
21989 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
21990     
21991     
21992     container_method : 'getPopoverHeader' 
21993     
21994      
21995     
21996     
21997    
21998 });
21999
22000  
22001
22002  /*
22003  * - LGPL
22004  *
22005  * Progress
22006  * 
22007  */
22008
22009 /**
22010  * @class Roo.bootstrap.Progress
22011  * @extends Roo.bootstrap.Component
22012  * @children Roo.bootstrap.ProgressBar
22013  * Bootstrap Progress class
22014  * @cfg {Boolean} striped striped of the progress bar
22015  * @cfg {Boolean} active animated of the progress bar
22016  * 
22017  * 
22018  * @constructor
22019  * Create a new Progress
22020  * @param {Object} config The config object
22021  */
22022
22023 Roo.bootstrap.Progress = function(config){
22024     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22025 };
22026
22027 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22028     
22029     striped : false,
22030     active: false,
22031     
22032     getAutoCreate : function(){
22033         var cfg = {
22034             tag: 'div',
22035             cls: 'progress'
22036         };
22037         
22038         
22039         if(this.striped){
22040             cfg.cls += ' progress-striped';
22041         }
22042       
22043         if(this.active){
22044             cfg.cls += ' active';
22045         }
22046         
22047         
22048         return cfg;
22049     }
22050    
22051 });
22052
22053  
22054
22055  /*
22056  * - LGPL
22057  *
22058  * ProgressBar
22059  * 
22060  */
22061
22062 /**
22063  * @class Roo.bootstrap.ProgressBar
22064  * @extends Roo.bootstrap.Component
22065  * Bootstrap ProgressBar class
22066  * @cfg {Number} aria_valuenow aria-value now
22067  * @cfg {Number} aria_valuemin aria-value min
22068  * @cfg {Number} aria_valuemax aria-value max
22069  * @cfg {String} label label for the progress bar
22070  * @cfg {String} panel (success | info | warning | danger )
22071  * @cfg {String} role role of the progress bar
22072  * @cfg {String} sr_only text
22073  * 
22074  * 
22075  * @constructor
22076  * Create a new ProgressBar
22077  * @param {Object} config The config object
22078  */
22079
22080 Roo.bootstrap.ProgressBar = function(config){
22081     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22082 };
22083
22084 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22085     
22086     aria_valuenow : 0,
22087     aria_valuemin : 0,
22088     aria_valuemax : 100,
22089     label : false,
22090     panel : false,
22091     role : false,
22092     sr_only: false,
22093     
22094     getAutoCreate : function()
22095     {
22096         
22097         var cfg = {
22098             tag: 'div',
22099             cls: 'progress-bar',
22100             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22101         };
22102         
22103         if(this.sr_only){
22104             cfg.cn = {
22105                 tag: 'span',
22106                 cls: 'sr-only',
22107                 html: this.sr_only
22108             }
22109         }
22110         
22111         if(this.role){
22112             cfg.role = this.role;
22113         }
22114         
22115         if(this.aria_valuenow){
22116             cfg['aria-valuenow'] = this.aria_valuenow;
22117         }
22118         
22119         if(this.aria_valuemin){
22120             cfg['aria-valuemin'] = this.aria_valuemin;
22121         }
22122         
22123         if(this.aria_valuemax){
22124             cfg['aria-valuemax'] = this.aria_valuemax;
22125         }
22126         
22127         if(this.label && !this.sr_only){
22128             cfg.html = this.label;
22129         }
22130         
22131         if(this.panel){
22132             cfg.cls += ' progress-bar-' + this.panel;
22133         }
22134         
22135         return cfg;
22136     },
22137     
22138     update : function(aria_valuenow)
22139     {
22140         this.aria_valuenow = aria_valuenow;
22141         
22142         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22143     }
22144    
22145 });
22146
22147  
22148
22149  /**
22150  * @class Roo.bootstrap.TabGroup
22151  * @extends Roo.bootstrap.Column
22152  * @children Roo.bootstrap.TabPanel
22153  * Bootstrap Column class
22154  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22155  * @cfg {Boolean} carousel true to make the group behave like a carousel
22156  * @cfg {Boolean} bullets show bullets for the panels
22157  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22158  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22159  * @cfg {Boolean} showarrow (true|false) show arrow default true
22160  * 
22161  * @constructor
22162  * Create a new TabGroup
22163  * @param {Object} config The config object
22164  */
22165
22166 Roo.bootstrap.TabGroup = function(config){
22167     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22168     if (!this.navId) {
22169         this.navId = Roo.id();
22170     }
22171     this.tabs = [];
22172     Roo.bootstrap.TabGroup.register(this);
22173     
22174 };
22175
22176 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22177     
22178     carousel : false,
22179     transition : false,
22180     bullets : 0,
22181     timer : 0,
22182     autoslide : false,
22183     slideFn : false,
22184     slideOnTouch : false,
22185     showarrow : true,
22186     
22187     getAutoCreate : function()
22188     {
22189         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22190         
22191         cfg.cls += ' tab-content';
22192         
22193         if (this.carousel) {
22194             cfg.cls += ' carousel slide';
22195             
22196             cfg.cn = [{
22197                cls : 'carousel-inner',
22198                cn : []
22199             }];
22200         
22201             if(this.bullets  && !Roo.isTouch){
22202                 
22203                 var bullets = {
22204                     cls : 'carousel-bullets',
22205                     cn : []
22206                 };
22207                
22208                 if(this.bullets_cls){
22209                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22210                 }
22211                 
22212                 bullets.cn.push({
22213                     cls : 'clear'
22214                 });
22215                 
22216                 cfg.cn[0].cn.push(bullets);
22217             }
22218             
22219             if(this.showarrow){
22220                 cfg.cn[0].cn.push({
22221                     tag : 'div',
22222                     class : 'carousel-arrow',
22223                     cn : [
22224                         {
22225                             tag : 'div',
22226                             class : 'carousel-prev',
22227                             cn : [
22228                                 {
22229                                     tag : 'i',
22230                                     class : 'fa fa-chevron-left'
22231                                 }
22232                             ]
22233                         },
22234                         {
22235                             tag : 'div',
22236                             class : 'carousel-next',
22237                             cn : [
22238                                 {
22239                                     tag : 'i',
22240                                     class : 'fa fa-chevron-right'
22241                                 }
22242                             ]
22243                         }
22244                     ]
22245                 });
22246             }
22247             
22248         }
22249         
22250         return cfg;
22251     },
22252     
22253     initEvents:  function()
22254     {
22255 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22256 //            this.el.on("touchstart", this.onTouchStart, this);
22257 //        }
22258         
22259         if(this.autoslide){
22260             var _this = this;
22261             
22262             this.slideFn = window.setInterval(function() {
22263                 _this.showPanelNext();
22264             }, this.timer);
22265         }
22266         
22267         if(this.showarrow){
22268             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22269             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22270         }
22271         
22272         
22273     },
22274     
22275 //    onTouchStart : function(e, el, o)
22276 //    {
22277 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22278 //            return;
22279 //        }
22280 //        
22281 //        this.showPanelNext();
22282 //    },
22283     
22284     
22285     getChildContainer : function()
22286     {
22287         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22288     },
22289     
22290     /**
22291     * register a Navigation item
22292     * @param {Roo.bootstrap.nav.Item} the navitem to add
22293     */
22294     register : function(item)
22295     {
22296         this.tabs.push( item);
22297         item.navId = this.navId; // not really needed..
22298         this.addBullet();
22299     
22300     },
22301     
22302     getActivePanel : function()
22303     {
22304         var r = false;
22305         Roo.each(this.tabs, function(t) {
22306             if (t.active) {
22307                 r = t;
22308                 return false;
22309             }
22310             return null;
22311         });
22312         return r;
22313         
22314     },
22315     getPanelByName : function(n)
22316     {
22317         var r = false;
22318         Roo.each(this.tabs, function(t) {
22319             if (t.tabId == n) {
22320                 r = t;
22321                 return false;
22322             }
22323             return null;
22324         });
22325         return r;
22326     },
22327     indexOfPanel : function(p)
22328     {
22329         var r = false;
22330         Roo.each(this.tabs, function(t,i) {
22331             if (t.tabId == p.tabId) {
22332                 r = i;
22333                 return false;
22334             }
22335             return null;
22336         });
22337         return r;
22338     },
22339     /**
22340      * show a specific panel
22341      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22342      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22343      */
22344     showPanel : function (pan)
22345     {
22346         if(this.transition || typeof(pan) == 'undefined'){
22347             Roo.log("waiting for the transitionend");
22348             return false;
22349         }
22350         
22351         if (typeof(pan) == 'number') {
22352             pan = this.tabs[pan];
22353         }
22354         
22355         if (typeof(pan) == 'string') {
22356             pan = this.getPanelByName(pan);
22357         }
22358         
22359         var cur = this.getActivePanel();
22360         
22361         if(!pan || !cur){
22362             Roo.log('pan or acitve pan is undefined');
22363             return false;
22364         }
22365         
22366         if (pan.tabId == this.getActivePanel().tabId) {
22367             return true;
22368         }
22369         
22370         if (false === cur.fireEvent('beforedeactivate')) {
22371             return false;
22372         }
22373         
22374         if(this.bullets > 0 && !Roo.isTouch){
22375             this.setActiveBullet(this.indexOfPanel(pan));
22376         }
22377         
22378         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22379             
22380             //class="carousel-item carousel-item-next carousel-item-left"
22381             
22382             this.transition = true;
22383             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22384             var lr = dir == 'next' ? 'left' : 'right';
22385             pan.el.addClass(dir); // or prev
22386             pan.el.addClass('carousel-item-' + dir); // or prev
22387             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22388             cur.el.addClass(lr); // or right
22389             pan.el.addClass(lr);
22390             cur.el.addClass('carousel-item-' +lr); // or right
22391             pan.el.addClass('carousel-item-' +lr);
22392             
22393             
22394             var _this = this;
22395             cur.el.on('transitionend', function() {
22396                 Roo.log("trans end?");
22397                 
22398                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22399                 pan.setActive(true);
22400                 
22401                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22402                 cur.setActive(false);
22403                 
22404                 _this.transition = false;
22405                 
22406             }, this, { single:  true } );
22407             
22408             return true;
22409         }
22410         
22411         cur.setActive(false);
22412         pan.setActive(true);
22413         
22414         return true;
22415         
22416     },
22417     showPanelNext : function()
22418     {
22419         var i = this.indexOfPanel(this.getActivePanel());
22420         
22421         if (i >= this.tabs.length - 1 && !this.autoslide) {
22422             return;
22423         }
22424         
22425         if (i >= this.tabs.length - 1 && this.autoslide) {
22426             i = -1;
22427         }
22428         
22429         this.showPanel(this.tabs[i+1]);
22430     },
22431     
22432     showPanelPrev : function()
22433     {
22434         var i = this.indexOfPanel(this.getActivePanel());
22435         
22436         if (i  < 1 && !this.autoslide) {
22437             return;
22438         }
22439         
22440         if (i < 1 && this.autoslide) {
22441             i = this.tabs.length;
22442         }
22443         
22444         this.showPanel(this.tabs[i-1]);
22445     },
22446     
22447     
22448     addBullet: function()
22449     {
22450         if(!this.bullets || Roo.isTouch){
22451             return;
22452         }
22453         var ctr = this.el.select('.carousel-bullets',true).first();
22454         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22455         var bullet = ctr.createChild({
22456             cls : 'bullet bullet-' + i
22457         },ctr.dom.lastChild);
22458         
22459         
22460         var _this = this;
22461         
22462         bullet.on('click', (function(e, el, o, ii, t){
22463
22464             e.preventDefault();
22465
22466             this.showPanel(ii);
22467
22468             if(this.autoslide && this.slideFn){
22469                 clearInterval(this.slideFn);
22470                 this.slideFn = window.setInterval(function() {
22471                     _this.showPanelNext();
22472                 }, this.timer);
22473             }
22474
22475         }).createDelegate(this, [i, bullet], true));
22476                 
22477         
22478     },
22479      
22480     setActiveBullet : function(i)
22481     {
22482         if(Roo.isTouch){
22483             return;
22484         }
22485         
22486         Roo.each(this.el.select('.bullet', true).elements, function(el){
22487             el.removeClass('selected');
22488         });
22489
22490         var bullet = this.el.select('.bullet-' + i, true).first();
22491         
22492         if(!bullet){
22493             return;
22494         }
22495         
22496         bullet.addClass('selected');
22497     }
22498     
22499     
22500   
22501 });
22502
22503  
22504
22505  
22506  
22507 Roo.apply(Roo.bootstrap.TabGroup, {
22508     
22509     groups: {},
22510      /**
22511     * register a Navigation Group
22512     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22513     */
22514     register : function(navgrp)
22515     {
22516         this.groups[navgrp.navId] = navgrp;
22517         
22518     },
22519     /**
22520     * fetch a Navigation Group based on the navigation ID
22521     * if one does not exist , it will get created.
22522     * @param {string} the navgroup to add
22523     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22524     */
22525     get: function(navId) {
22526         if (typeof(this.groups[navId]) == 'undefined') {
22527             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22528         }
22529         return this.groups[navId] ;
22530     }
22531     
22532     
22533     
22534 });
22535
22536  /*
22537  * - LGPL
22538  *
22539  * TabPanel
22540  * 
22541  */
22542
22543 /**
22544  * @class Roo.bootstrap.TabPanel
22545  * @extends Roo.bootstrap.Component
22546  * @children Roo.bootstrap.Component
22547  * Bootstrap TabPanel class
22548  * @cfg {Boolean} active panel active
22549  * @cfg {String} html panel content
22550  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22551  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22552  * @cfg {String} href click to link..
22553  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22554  * 
22555  * 
22556  * @constructor
22557  * Create a new TabPanel
22558  * @param {Object} config The config object
22559  */
22560
22561 Roo.bootstrap.TabPanel = function(config){
22562     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22563     this.addEvents({
22564         /**
22565              * @event changed
22566              * Fires when the active status changes
22567              * @param {Roo.bootstrap.TabPanel} this
22568              * @param {Boolean} state the new state
22569             
22570          */
22571         'changed': true,
22572         /**
22573              * @event beforedeactivate
22574              * Fires before a tab is de-activated - can be used to do validation on a form.
22575              * @param {Roo.bootstrap.TabPanel} this
22576              * @return {Boolean} false if there is an error
22577             
22578          */
22579         'beforedeactivate': true
22580      });
22581     
22582     this.tabId = this.tabId || Roo.id();
22583   
22584 };
22585
22586 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22587     
22588     active: false,
22589     html: false,
22590     tabId: false,
22591     navId : false,
22592     href : '',
22593     touchSlide : false,
22594     getAutoCreate : function(){
22595         
22596         
22597         var cfg = {
22598             tag: 'div',
22599             // item is needed for carousel - not sure if it has any effect otherwise
22600             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22601             html: this.html || ''
22602         };
22603         
22604         if(this.active){
22605             cfg.cls += ' active';
22606         }
22607         
22608         if(this.tabId){
22609             cfg.tabId = this.tabId;
22610         }
22611         
22612         
22613         
22614         return cfg;
22615     },
22616     
22617     initEvents:  function()
22618     {
22619         var p = this.parent();
22620         
22621         this.navId = this.navId || p.navId;
22622         
22623         if (typeof(this.navId) != 'undefined') {
22624             // not really needed.. but just in case.. parent should be a NavGroup.
22625             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22626             
22627             tg.register(this);
22628             
22629             var i = tg.tabs.length - 1;
22630             
22631             if(this.active && tg.bullets > 0 && i < tg.bullets){
22632                 tg.setActiveBullet(i);
22633             }
22634         }
22635         
22636         this.el.on('click', this.onClick, this);
22637         
22638         if(Roo.isTouch && this.touchSlide){
22639             this.el.on("touchstart", this.onTouchStart, this);
22640             this.el.on("touchmove", this.onTouchMove, this);
22641             this.el.on("touchend", this.onTouchEnd, this);
22642         }
22643         
22644     },
22645     
22646     onRender : function(ct, position)
22647     {
22648         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22649     },
22650     
22651     setActive : function(state)
22652     {
22653         Roo.log("panel - set active " + this.tabId + "=" + state);
22654         
22655         this.active = state;
22656         if (!state) {
22657             this.el.removeClass('active');
22658             
22659         } else  if (!this.el.hasClass('active')) {
22660             this.el.addClass('active');
22661         }
22662         
22663         this.fireEvent('changed', this, state);
22664     },
22665     
22666     onClick : function(e)
22667     {
22668         e.preventDefault();
22669         
22670         if(!this.href.length){
22671             return;
22672         }
22673         
22674         window.location.href = this.href;
22675     },
22676     
22677     startX : 0,
22678     startY : 0,
22679     endX : 0,
22680     endY : 0,
22681     swiping : false,
22682     
22683     onTouchStart : function(e)
22684     {
22685         this.swiping = false;
22686         
22687         this.startX = e.browserEvent.touches[0].clientX;
22688         this.startY = e.browserEvent.touches[0].clientY;
22689     },
22690     
22691     onTouchMove : function(e)
22692     {
22693         this.swiping = true;
22694         
22695         this.endX = e.browserEvent.touches[0].clientX;
22696         this.endY = e.browserEvent.touches[0].clientY;
22697     },
22698     
22699     onTouchEnd : function(e)
22700     {
22701         if(!this.swiping){
22702             this.onClick(e);
22703             return;
22704         }
22705         
22706         var tabGroup = this.parent();
22707         
22708         if(this.endX > this.startX){ // swiping right
22709             tabGroup.showPanelPrev();
22710             return;
22711         }
22712         
22713         if(this.startX > this.endX){ // swiping left
22714             tabGroup.showPanelNext();
22715             return;
22716         }
22717     }
22718     
22719     
22720 });
22721  
22722
22723  
22724
22725  /*
22726  * - LGPL
22727  *
22728  * DateField
22729  * 
22730  */
22731
22732 /**
22733  * @class Roo.bootstrap.form.DateField
22734  * @extends Roo.bootstrap.form.Input
22735  * Bootstrap DateField class
22736  * @cfg {Number} weekStart default 0
22737  * @cfg {String} viewMode default empty, (months|years)
22738  * @cfg {String} minViewMode default empty, (months|years)
22739  * @cfg {Number} startDate default -Infinity
22740  * @cfg {Number} endDate default Infinity
22741  * @cfg {Boolean} todayHighlight default false
22742  * @cfg {Boolean} todayBtn default false
22743  * @cfg {Boolean} calendarWeeks default false
22744  * @cfg {Object} daysOfWeekDisabled default empty
22745  * @cfg {Boolean} singleMode default false (true | false)
22746  * 
22747  * @cfg {Boolean} keyboardNavigation default true
22748  * @cfg {String} language default en
22749  * 
22750  * @constructor
22751  * Create a new DateField
22752  * @param {Object} config The config object
22753  */
22754
22755 Roo.bootstrap.form.DateField = function(config){
22756     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22757      this.addEvents({
22758             /**
22759              * @event show
22760              * Fires when this field show.
22761              * @param {Roo.bootstrap.form.DateField} this
22762              * @param {Mixed} date The date value
22763              */
22764             show : true,
22765             /**
22766              * @event show
22767              * Fires when this field hide.
22768              * @param {Roo.bootstrap.form.DateField} this
22769              * @param {Mixed} date The date value
22770              */
22771             hide : true,
22772             /**
22773              * @event select
22774              * Fires when select a date.
22775              * @param {Roo.bootstrap.form.DateField} this
22776              * @param {Mixed} date The date value
22777              */
22778             select : true,
22779             /**
22780              * @event beforeselect
22781              * Fires when before select a date.
22782              * @param {Roo.bootstrap.form.DateField} this
22783              * @param {Mixed} date The date value
22784              */
22785             beforeselect : true
22786         });
22787 };
22788
22789 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22790     
22791     /**
22792      * @cfg {String} format
22793      * The default date format string which can be overriden for localization support.  The format must be
22794      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22795      */
22796     format : "m/d/y",
22797     /**
22798      * @cfg {String} altFormats
22799      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22800      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22801      */
22802     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22803     
22804     weekStart : 0,
22805     
22806     viewMode : '',
22807     
22808     minViewMode : '',
22809     
22810     todayHighlight : false,
22811     
22812     todayBtn: false,
22813     
22814     language: 'en',
22815     
22816     keyboardNavigation: true,
22817     
22818     calendarWeeks: false,
22819     
22820     startDate: -Infinity,
22821     
22822     endDate: Infinity,
22823     
22824     daysOfWeekDisabled: [],
22825     
22826     _events: [],
22827     
22828     singleMode : false,
22829     
22830     UTCDate: function()
22831     {
22832         return new Date(Date.UTC.apply(Date, arguments));
22833     },
22834     
22835     UTCToday: function()
22836     {
22837         var today = new Date();
22838         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22839     },
22840     
22841     getDate: function() {
22842             var d = this.getUTCDate();
22843             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22844     },
22845     
22846     getUTCDate: function() {
22847             return this.date;
22848     },
22849     
22850     setDate: function(d) {
22851             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22852     },
22853     
22854     setUTCDate: function(d) {
22855             this.date = d;
22856             this.setValue(this.formatDate(this.date));
22857     },
22858         
22859     onRender: function(ct, position)
22860     {
22861         
22862         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22863         
22864         this.language = this.language || 'en';
22865         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22866         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22867         
22868         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22869         this.format = this.format || 'm/d/y';
22870         this.isInline = false;
22871         this.isInput = true;
22872         this.component = this.el.select('.add-on', true).first() || false;
22873         this.component = (this.component && this.component.length === 0) ? false : this.component;
22874         this.hasInput = this.component && this.inputEl().length;
22875         
22876         if (typeof(this.minViewMode === 'string')) {
22877             switch (this.minViewMode) {
22878                 case 'months':
22879                     this.minViewMode = 1;
22880                     break;
22881                 case 'years':
22882                     this.minViewMode = 2;
22883                     break;
22884                 default:
22885                     this.minViewMode = 0;
22886                     break;
22887             }
22888         }
22889         
22890         if (typeof(this.viewMode === 'string')) {
22891             switch (this.viewMode) {
22892                 case 'months':
22893                     this.viewMode = 1;
22894                     break;
22895                 case 'years':
22896                     this.viewMode = 2;
22897                     break;
22898                 default:
22899                     this.viewMode = 0;
22900                     break;
22901             }
22902         }
22903                 
22904         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22905         
22906 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22907         
22908         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22909         
22910         this.picker().on('mousedown', this.onMousedown, this);
22911         this.picker().on('click', this.onClick, this);
22912         
22913         this.picker().addClass('datepicker-dropdown');
22914         
22915         this.startViewMode = this.viewMode;
22916         
22917         if(this.singleMode){
22918             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22919                 v.setVisibilityMode(Roo.Element.DISPLAY);
22920                 v.hide();
22921             });
22922             
22923             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22924                 v.setStyle('width', '189px');
22925             });
22926         }
22927         
22928         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22929             if(!this.calendarWeeks){
22930                 v.remove();
22931                 return;
22932             }
22933             
22934             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22935             v.attr('colspan', function(i, val){
22936                 return parseInt(val) + 1;
22937             });
22938         });
22939                         
22940         
22941         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22942         
22943         this.setStartDate(this.startDate);
22944         this.setEndDate(this.endDate);
22945         
22946         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22947         
22948         this.fillDow();
22949         this.fillMonths();
22950         this.update();
22951         this.showMode();
22952         
22953         if(this.isInline) {
22954             this.showPopup();
22955         }
22956     },
22957     
22958     picker : function()
22959     {
22960         return this.pickerEl;
22961 //        return this.el.select('.datepicker', true).first();
22962     },
22963     
22964     fillDow: function()
22965     {
22966         var dowCnt = this.weekStart;
22967         
22968         var dow = {
22969             tag: 'tr',
22970             cn: [
22971                 
22972             ]
22973         };
22974         
22975         if(this.calendarWeeks){
22976             dow.cn.push({
22977                 tag: 'th',
22978                 cls: 'cw',
22979                 html: '&nbsp;'
22980             })
22981         }
22982         
22983         while (dowCnt < this.weekStart + 7) {
22984             dow.cn.push({
22985                 tag: 'th',
22986                 cls: 'dow',
22987                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22988             });
22989         }
22990         
22991         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22992     },
22993     
22994     fillMonths: function()
22995     {    
22996         var i = 0;
22997         var months = this.picker().select('>.datepicker-months td', true).first();
22998         
22999         months.dom.innerHTML = '';
23000         
23001         while (i < 12) {
23002             var month = {
23003                 tag: 'span',
23004                 cls: 'month',
23005                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23006             };
23007             
23008             months.createChild(month);
23009         }
23010         
23011     },
23012     
23013     update: function()
23014     {
23015         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;
23016         
23017         if (this.date < this.startDate) {
23018             this.viewDate = new Date(this.startDate);
23019         } else if (this.date > this.endDate) {
23020             this.viewDate = new Date(this.endDate);
23021         } else {
23022             this.viewDate = new Date(this.date);
23023         }
23024         
23025         this.fill();
23026     },
23027     
23028     fill: function() 
23029     {
23030         var d = new Date(this.viewDate),
23031                 year = d.getUTCFullYear(),
23032                 month = d.getUTCMonth(),
23033                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23034                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23035                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23036                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23037                 currentDate = this.date && this.date.valueOf(),
23038                 today = this.UTCToday();
23039         
23040         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23041         
23042 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23043         
23044 //        this.picker.select('>tfoot th.today').
23045 //                                              .text(dates[this.language].today)
23046 //                                              .toggle(this.todayBtn !== false);
23047     
23048         this.updateNavArrows();
23049         this.fillMonths();
23050                                                 
23051         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23052         
23053         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23054          
23055         prevMonth.setUTCDate(day);
23056         
23057         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23058         
23059         var nextMonth = new Date(prevMonth);
23060         
23061         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23062         
23063         nextMonth = nextMonth.valueOf();
23064         
23065         var fillMonths = false;
23066         
23067         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23068         
23069         while(prevMonth.valueOf() <= nextMonth) {
23070             var clsName = '';
23071             
23072             if (prevMonth.getUTCDay() === this.weekStart) {
23073                 if(fillMonths){
23074                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23075                 }
23076                     
23077                 fillMonths = {
23078                     tag: 'tr',
23079                     cn: []
23080                 };
23081                 
23082                 if(this.calendarWeeks){
23083                     // ISO 8601: First week contains first thursday.
23084                     // ISO also states week starts on Monday, but we can be more abstract here.
23085                     var
23086                     // Start of current week: based on weekstart/current date
23087                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23088                     // Thursday of this week
23089                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23090                     // First Thursday of year, year from thursday
23091                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23092                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23093                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23094                     
23095                     fillMonths.cn.push({
23096                         tag: 'td',
23097                         cls: 'cw',
23098                         html: calWeek
23099                     });
23100                 }
23101             }
23102             
23103             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23104                 clsName += ' old';
23105             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23106                 clsName += ' new';
23107             }
23108             if (this.todayHighlight &&
23109                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23110                 prevMonth.getUTCMonth() == today.getMonth() &&
23111                 prevMonth.getUTCDate() == today.getDate()) {
23112                 clsName += ' today';
23113             }
23114             
23115             if (currentDate && prevMonth.valueOf() === currentDate) {
23116                 clsName += ' active';
23117             }
23118             
23119             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23120                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23121                     clsName += ' disabled';
23122             }
23123             
23124             fillMonths.cn.push({
23125                 tag: 'td',
23126                 cls: 'day ' + clsName,
23127                 html: prevMonth.getDate()
23128             });
23129             
23130             prevMonth.setDate(prevMonth.getDate()+1);
23131         }
23132           
23133         var currentYear = this.date && this.date.getUTCFullYear();
23134         var currentMonth = this.date && this.date.getUTCMonth();
23135         
23136         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23137         
23138         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23139             v.removeClass('active');
23140             
23141             if(currentYear === year && k === currentMonth){
23142                 v.addClass('active');
23143             }
23144             
23145             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23146                 v.addClass('disabled');
23147             }
23148             
23149         });
23150         
23151         
23152         year = parseInt(year/10, 10) * 10;
23153         
23154         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23155         
23156         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23157         
23158         year -= 1;
23159         for (var i = -1; i < 11; i++) {
23160             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23161                 tag: 'span',
23162                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23163                 html: year
23164             });
23165             
23166             year += 1;
23167         }
23168     },
23169     
23170     showMode: function(dir) 
23171     {
23172         if (dir) {
23173             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23174         }
23175         
23176         Roo.each(this.picker().select('>div',true).elements, function(v){
23177             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23178             v.hide();
23179         });
23180         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23181     },
23182     
23183     place: function()
23184     {
23185         if(this.isInline) {
23186             return;
23187         }
23188         
23189         this.picker().removeClass(['bottom', 'top']);
23190         
23191         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23192             /*
23193              * place to the top of element!
23194              *
23195              */
23196             
23197             this.picker().addClass('top');
23198             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23199             
23200             return;
23201         }
23202         
23203         this.picker().addClass('bottom');
23204         
23205         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23206     },
23207     
23208     parseDate : function(value)
23209     {
23210         if(!value || value instanceof Date){
23211             return value;
23212         }
23213         var v = Date.parseDate(value, this.format);
23214         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23215             v = Date.parseDate(value, 'Y-m-d');
23216         }
23217         if(!v && this.altFormats){
23218             if(!this.altFormatsArray){
23219                 this.altFormatsArray = this.altFormats.split("|");
23220             }
23221             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23222                 v = Date.parseDate(value, this.altFormatsArray[i]);
23223             }
23224         }
23225         return v;
23226     },
23227     
23228     formatDate : function(date, fmt)
23229     {   
23230         return (!date || !(date instanceof Date)) ?
23231         date : date.dateFormat(fmt || this.format);
23232     },
23233     
23234     onFocus : function()
23235     {
23236         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23237         this.showPopup();
23238     },
23239     
23240     onBlur : function()
23241     {
23242         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23243         
23244         var d = this.inputEl().getValue();
23245         
23246         this.setValue(d);
23247                 
23248         this.hidePopup();
23249     },
23250     
23251     showPopup : function()
23252     {
23253         this.picker().show();
23254         this.update();
23255         this.place();
23256         
23257         this.fireEvent('showpopup', this, this.date);
23258     },
23259     
23260     hidePopup : function()
23261     {
23262         if(this.isInline) {
23263             return;
23264         }
23265         this.picker().hide();
23266         this.viewMode = this.startViewMode;
23267         this.showMode();
23268         
23269         this.fireEvent('hidepopup', this, this.date);
23270         
23271     },
23272     
23273     onMousedown: function(e)
23274     {
23275         e.stopPropagation();
23276         e.preventDefault();
23277     },
23278     
23279     keyup: function(e)
23280     {
23281         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23282         this.update();
23283     },
23284
23285     setValue: function(v)
23286     {
23287         if(this.fireEvent('beforeselect', this, v) !== false){
23288             var d = new Date(this.parseDate(v) ).clearTime();
23289         
23290             if(isNaN(d.getTime())){
23291                 this.date = this.viewDate = '';
23292                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23293                 return;
23294             }
23295
23296             v = this.formatDate(d);
23297
23298             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23299
23300             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23301
23302             this.update();
23303
23304             this.fireEvent('select', this, this.date);
23305         }
23306     },
23307     
23308     getValue: function()
23309     {
23310         return this.formatDate(this.date);
23311     },
23312     
23313     fireKey: function(e)
23314     {
23315         if (!this.picker().isVisible()){
23316             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23317                 this.showPopup();
23318             }
23319             return;
23320         }
23321         
23322         var dateChanged = false,
23323         dir, day, month,
23324         newDate, newViewDate;
23325         
23326         switch(e.keyCode){
23327             case 27: // escape
23328                 this.hidePopup();
23329                 e.preventDefault();
23330                 break;
23331             case 37: // left
23332             case 39: // right
23333                 if (!this.keyboardNavigation) {
23334                     break;
23335                 }
23336                 dir = e.keyCode == 37 ? -1 : 1;
23337                 
23338                 if (e.ctrlKey){
23339                     newDate = this.moveYear(this.date, dir);
23340                     newViewDate = this.moveYear(this.viewDate, dir);
23341                 } else if (e.shiftKey){
23342                     newDate = this.moveMonth(this.date, dir);
23343                     newViewDate = this.moveMonth(this.viewDate, dir);
23344                 } else {
23345                     newDate = new Date(this.date);
23346                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23347                     newViewDate = new Date(this.viewDate);
23348                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23349                 }
23350                 if (this.dateWithinRange(newDate)){
23351                     this.date = newDate;
23352                     this.viewDate = newViewDate;
23353                     this.setValue(this.formatDate(this.date));
23354 //                    this.update();
23355                     e.preventDefault();
23356                     dateChanged = true;
23357                 }
23358                 break;
23359             case 38: // up
23360             case 40: // down
23361                 if (!this.keyboardNavigation) {
23362                     break;
23363                 }
23364                 dir = e.keyCode == 38 ? -1 : 1;
23365                 if (e.ctrlKey){
23366                     newDate = this.moveYear(this.date, dir);
23367                     newViewDate = this.moveYear(this.viewDate, dir);
23368                 } else if (e.shiftKey){
23369                     newDate = this.moveMonth(this.date, dir);
23370                     newViewDate = this.moveMonth(this.viewDate, dir);
23371                 } else {
23372                     newDate = new Date(this.date);
23373                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23374                     newViewDate = new Date(this.viewDate);
23375                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23376                 }
23377                 if (this.dateWithinRange(newDate)){
23378                     this.date = newDate;
23379                     this.viewDate = newViewDate;
23380                     this.setValue(this.formatDate(this.date));
23381 //                    this.update();
23382                     e.preventDefault();
23383                     dateChanged = true;
23384                 }
23385                 break;
23386             case 13: // enter
23387                 this.setValue(this.formatDate(this.date));
23388                 this.hidePopup();
23389                 e.preventDefault();
23390                 break;
23391             case 9: // tab
23392                 this.setValue(this.formatDate(this.date));
23393                 this.hidePopup();
23394                 break;
23395             case 16: // shift
23396             case 17: // ctrl
23397             case 18: // alt
23398                 break;
23399             default :
23400                 this.hidePopup();
23401                 
23402         }
23403     },
23404     
23405     
23406     onClick: function(e) 
23407     {
23408         e.stopPropagation();
23409         e.preventDefault();
23410         
23411         var target = e.getTarget();
23412         
23413         if(target.nodeName.toLowerCase() === 'i'){
23414             target = Roo.get(target).dom.parentNode;
23415         }
23416         
23417         var nodeName = target.nodeName;
23418         var className = target.className;
23419         var html = target.innerHTML;
23420         //Roo.log(nodeName);
23421         
23422         switch(nodeName.toLowerCase()) {
23423             case 'th':
23424                 switch(className) {
23425                     case 'switch':
23426                         this.showMode(1);
23427                         break;
23428                     case 'prev':
23429                     case 'next':
23430                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23431                         switch(this.viewMode){
23432                                 case 0:
23433                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23434                                         break;
23435                                 case 1:
23436                                 case 2:
23437                                         this.viewDate = this.moveYear(this.viewDate, dir);
23438                                         break;
23439                         }
23440                         this.fill();
23441                         break;
23442                     case 'today':
23443                         var date = new Date();
23444                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23445 //                        this.fill()
23446                         this.setValue(this.formatDate(this.date));
23447                         
23448                         this.hidePopup();
23449                         break;
23450                 }
23451                 break;
23452             case 'span':
23453                 if (className.indexOf('disabled') < 0) {
23454                 if (!this.viewDate) {
23455                     this.viewDate = new Date();
23456                 }
23457                 this.viewDate.setUTCDate(1);
23458                     if (className.indexOf('month') > -1) {
23459                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23460                     } else {
23461                         var year = parseInt(html, 10) || 0;
23462                         this.viewDate.setUTCFullYear(year);
23463                         
23464                     }
23465                     
23466                     if(this.singleMode){
23467                         this.setValue(this.formatDate(this.viewDate));
23468                         this.hidePopup();
23469                         return;
23470                     }
23471                     
23472                     this.showMode(-1);
23473                     this.fill();
23474                 }
23475                 break;
23476                 
23477             case 'td':
23478                 //Roo.log(className);
23479                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23480                     var day = parseInt(html, 10) || 1;
23481                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23482                         month = (this.viewDate || new Date()).getUTCMonth();
23483
23484                     if (className.indexOf('old') > -1) {
23485                         if(month === 0 ){
23486                             month = 11;
23487                             year -= 1;
23488                         }else{
23489                             month -= 1;
23490                         }
23491                     } else if (className.indexOf('new') > -1) {
23492                         if (month == 11) {
23493                             month = 0;
23494                             year += 1;
23495                         } else {
23496                             month += 1;
23497                         }
23498                     }
23499                     //Roo.log([year,month,day]);
23500                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23501                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23502 //                    this.fill();
23503                     //Roo.log(this.formatDate(this.date));
23504                     this.setValue(this.formatDate(this.date));
23505                     this.hidePopup();
23506                 }
23507                 break;
23508         }
23509     },
23510     
23511     setStartDate: function(startDate)
23512     {
23513         this.startDate = startDate || -Infinity;
23514         if (this.startDate !== -Infinity) {
23515             this.startDate = this.parseDate(this.startDate);
23516         }
23517         this.update();
23518         this.updateNavArrows();
23519     },
23520
23521     setEndDate: function(endDate)
23522     {
23523         this.endDate = endDate || Infinity;
23524         if (this.endDate !== Infinity) {
23525             this.endDate = this.parseDate(this.endDate);
23526         }
23527         this.update();
23528         this.updateNavArrows();
23529     },
23530     
23531     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23532     {
23533         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23534         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23535             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23536         }
23537         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23538             return parseInt(d, 10);
23539         });
23540         this.update();
23541         this.updateNavArrows();
23542     },
23543     
23544     updateNavArrows: function() 
23545     {
23546         if(this.singleMode){
23547             return;
23548         }
23549         
23550         var d = new Date(this.viewDate),
23551         year = d.getUTCFullYear(),
23552         month = d.getUTCMonth();
23553         
23554         Roo.each(this.picker().select('.prev', true).elements, function(v){
23555             v.show();
23556             switch (this.viewMode) {
23557                 case 0:
23558
23559                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23560                         v.hide();
23561                     }
23562                     break;
23563                 case 1:
23564                 case 2:
23565                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23566                         v.hide();
23567                     }
23568                     break;
23569             }
23570         });
23571         
23572         Roo.each(this.picker().select('.next', true).elements, function(v){
23573             v.show();
23574             switch (this.viewMode) {
23575                 case 0:
23576
23577                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23578                         v.hide();
23579                     }
23580                     break;
23581                 case 1:
23582                 case 2:
23583                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23584                         v.hide();
23585                     }
23586                     break;
23587             }
23588         })
23589     },
23590     
23591     moveMonth: function(date, dir)
23592     {
23593         if (!dir) {
23594             return date;
23595         }
23596         var new_date = new Date(date.valueOf()),
23597         day = new_date.getUTCDate(),
23598         month = new_date.getUTCMonth(),
23599         mag = Math.abs(dir),
23600         new_month, test;
23601         dir = dir > 0 ? 1 : -1;
23602         if (mag == 1){
23603             test = dir == -1
23604             // If going back one month, make sure month is not current month
23605             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23606             ? function(){
23607                 return new_date.getUTCMonth() == month;
23608             }
23609             // If going forward one month, make sure month is as expected
23610             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23611             : function(){
23612                 return new_date.getUTCMonth() != new_month;
23613             };
23614             new_month = month + dir;
23615             new_date.setUTCMonth(new_month);
23616             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23617             if (new_month < 0 || new_month > 11) {
23618                 new_month = (new_month + 12) % 12;
23619             }
23620         } else {
23621             // For magnitudes >1, move one month at a time...
23622             for (var i=0; i<mag; i++) {
23623                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23624                 new_date = this.moveMonth(new_date, dir);
23625             }
23626             // ...then reset the day, keeping it in the new month
23627             new_month = new_date.getUTCMonth();
23628             new_date.setUTCDate(day);
23629             test = function(){
23630                 return new_month != new_date.getUTCMonth();
23631             };
23632         }
23633         // Common date-resetting loop -- if date is beyond end of month, make it
23634         // end of month
23635         while (test()){
23636             new_date.setUTCDate(--day);
23637             new_date.setUTCMonth(new_month);
23638         }
23639         return new_date;
23640     },
23641
23642     moveYear: function(date, dir)
23643     {
23644         return this.moveMonth(date, dir*12);
23645     },
23646
23647     dateWithinRange: function(date)
23648     {
23649         return date >= this.startDate && date <= this.endDate;
23650     },
23651
23652     
23653     remove: function() 
23654     {
23655         this.picker().remove();
23656     },
23657     
23658     validateValue : function(value)
23659     {
23660         if(this.getVisibilityEl().hasClass('hidden')){
23661             return true;
23662         }
23663         
23664         if(value.length < 1)  {
23665             if(this.allowBlank){
23666                 return true;
23667             }
23668             return false;
23669         }
23670         
23671         if(value.length < this.minLength){
23672             return false;
23673         }
23674         if(value.length > this.maxLength){
23675             return false;
23676         }
23677         if(this.vtype){
23678             var vt = Roo.form.VTypes;
23679             if(!vt[this.vtype](value, this)){
23680                 return false;
23681             }
23682         }
23683         if(typeof this.validator == "function"){
23684             var msg = this.validator(value);
23685             if(msg !== true){
23686                 return false;
23687             }
23688         }
23689         
23690         if(this.regex && !this.regex.test(value)){
23691             return false;
23692         }
23693         
23694         if(typeof(this.parseDate(value)) == 'undefined'){
23695             return false;
23696         }
23697         
23698         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23699             return false;
23700         }      
23701         
23702         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23703             return false;
23704         } 
23705         
23706         
23707         return true;
23708     },
23709     
23710     reset : function()
23711     {
23712         this.date = this.viewDate = '';
23713         
23714         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23715     }
23716    
23717 });
23718
23719 Roo.apply(Roo.bootstrap.form.DateField,  {
23720     
23721     head : {
23722         tag: 'thead',
23723         cn: [
23724         {
23725             tag: 'tr',
23726             cn: [
23727             {
23728                 tag: 'th',
23729                 cls: 'prev',
23730                 html: '<i class="fa fa-arrow-left"/>'
23731             },
23732             {
23733                 tag: 'th',
23734                 cls: 'switch',
23735                 colspan: '5'
23736             },
23737             {
23738                 tag: 'th',
23739                 cls: 'next',
23740                 html: '<i class="fa fa-arrow-right"/>'
23741             }
23742
23743             ]
23744         }
23745         ]
23746     },
23747     
23748     content : {
23749         tag: 'tbody',
23750         cn: [
23751         {
23752             tag: 'tr',
23753             cn: [
23754             {
23755                 tag: 'td',
23756                 colspan: '7'
23757             }
23758             ]
23759         }
23760         ]
23761     },
23762     
23763     footer : {
23764         tag: 'tfoot',
23765         cn: [
23766         {
23767             tag: 'tr',
23768             cn: [
23769             {
23770                 tag: 'th',
23771                 colspan: '7',
23772                 cls: 'today'
23773             }
23774                     
23775             ]
23776         }
23777         ]
23778     },
23779     
23780     dates:{
23781         en: {
23782             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23783             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23784             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23785             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23786             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23787             today: "Today"
23788         }
23789     },
23790     
23791     modes: [
23792     {
23793         clsName: 'days',
23794         navFnc: 'Month',
23795         navStep: 1
23796     },
23797     {
23798         clsName: 'months',
23799         navFnc: 'FullYear',
23800         navStep: 1
23801     },
23802     {
23803         clsName: 'years',
23804         navFnc: 'FullYear',
23805         navStep: 10
23806     }]
23807 });
23808
23809 Roo.apply(Roo.bootstrap.form.DateField,  {
23810   
23811     template : {
23812         tag: 'div',
23813         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23814         cn: [
23815         {
23816             tag: 'div',
23817             cls: 'datepicker-days',
23818             cn: [
23819             {
23820                 tag: 'table',
23821                 cls: 'table-condensed',
23822                 cn:[
23823                 Roo.bootstrap.form.DateField.head,
23824                 {
23825                     tag: 'tbody'
23826                 },
23827                 Roo.bootstrap.form.DateField.footer
23828                 ]
23829             }
23830             ]
23831         },
23832         {
23833             tag: 'div',
23834             cls: 'datepicker-months',
23835             cn: [
23836             {
23837                 tag: 'table',
23838                 cls: 'table-condensed',
23839                 cn:[
23840                 Roo.bootstrap.form.DateField.head,
23841                 Roo.bootstrap.form.DateField.content,
23842                 Roo.bootstrap.form.DateField.footer
23843                 ]
23844             }
23845             ]
23846         },
23847         {
23848             tag: 'div',
23849             cls: 'datepicker-years',
23850             cn: [
23851             {
23852                 tag: 'table',
23853                 cls: 'table-condensed',
23854                 cn:[
23855                 Roo.bootstrap.form.DateField.head,
23856                 Roo.bootstrap.form.DateField.content,
23857                 Roo.bootstrap.form.DateField.footer
23858                 ]
23859             }
23860             ]
23861         }
23862         ]
23863     }
23864 });
23865
23866  
23867
23868  /*
23869  * - LGPL
23870  *
23871  * TimeField
23872  * 
23873  */
23874
23875 /**
23876  * @class Roo.bootstrap.form.TimeField
23877  * @extends Roo.bootstrap.form.Input
23878  * Bootstrap DateField class
23879  * 
23880  * 
23881  * @constructor
23882  * Create a new TimeField
23883  * @param {Object} config The config object
23884  */
23885
23886 Roo.bootstrap.form.TimeField = function(config){
23887     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23888     this.addEvents({
23889             /**
23890              * @event show
23891              * Fires when this field show.
23892              * @param {Roo.bootstrap.form.DateField} thisthis
23893              * @param {Mixed} date The date value
23894              */
23895             show : true,
23896             /**
23897              * @event show
23898              * Fires when this field hide.
23899              * @param {Roo.bootstrap.form.DateField} this
23900              * @param {Mixed} date The date value
23901              */
23902             hide : true,
23903             /**
23904              * @event select
23905              * Fires when select a date.
23906              * @param {Roo.bootstrap.form.DateField} this
23907              * @param {Mixed} date The date value
23908              */
23909             select : true
23910         });
23911 };
23912
23913 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23914     
23915     /**
23916      * @cfg {String} format
23917      * The default time format string which can be overriden for localization support.  The format must be
23918      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23919      */
23920     format : "H:i",
23921
23922     getAutoCreate : function()
23923     {
23924         this.after = '<i class="fa far fa-clock"></i>';
23925         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23926         
23927          
23928     },
23929     onRender: function(ct, position)
23930     {
23931         
23932         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23933                 
23934         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23935         
23936         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23937         
23938         this.pop = this.picker().select('>.datepicker-time',true).first();
23939         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23940         
23941         this.picker().on('mousedown', this.onMousedown, this);
23942         this.picker().on('click', this.onClick, this);
23943         
23944         this.picker().addClass('datepicker-dropdown');
23945     
23946         this.fillTime();
23947         this.update();
23948             
23949         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23950         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23951         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23952         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23953         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23954         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23955
23956     },
23957     
23958     fireKey: function(e){
23959         if (!this.picker().isVisible()){
23960             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23961                 this.show();
23962             }
23963             return;
23964         }
23965
23966         e.preventDefault();
23967         
23968         switch(e.keyCode){
23969             case 27: // escape
23970                 this.hide();
23971                 break;
23972             case 37: // left
23973             case 39: // right
23974                 this.onTogglePeriod();
23975                 break;
23976             case 38: // up
23977                 this.onIncrementMinutes();
23978                 break;
23979             case 40: // down
23980                 this.onDecrementMinutes();
23981                 break;
23982             case 13: // enter
23983             case 9: // tab
23984                 this.setTime();
23985                 break;
23986         }
23987     },
23988     
23989     onClick: function(e) {
23990         e.stopPropagation();
23991         e.preventDefault();
23992     },
23993     
23994     picker : function()
23995     {
23996         return this.pickerEl;
23997     },
23998     
23999     fillTime: function()
24000     {    
24001         var time = this.pop.select('tbody', true).first();
24002         
24003         time.dom.innerHTML = '';
24004         
24005         time.createChild({
24006             tag: 'tr',
24007             cn: [
24008                 {
24009                     tag: 'td',
24010                     cn: [
24011                         {
24012                             tag: 'a',
24013                             href: '#',
24014                             cls: 'btn',
24015                             cn: [
24016                                 {
24017                                     tag: 'i',
24018                                     cls: 'hours-up fa fas fa-chevron-up'
24019                                 }
24020                             ]
24021                         } 
24022                     ]
24023                 },
24024                 {
24025                     tag: 'td',
24026                     cls: 'separator'
24027                 },
24028                 {
24029                     tag: 'td',
24030                     cn: [
24031                         {
24032                             tag: 'a',
24033                             href: '#',
24034                             cls: 'btn',
24035                             cn: [
24036                                 {
24037                                     tag: 'i',
24038                                     cls: 'minutes-up fa fas fa-chevron-up'
24039                                 }
24040                             ]
24041                         }
24042                     ]
24043                 },
24044                 {
24045                     tag: 'td',
24046                     cls: 'separator'
24047                 }
24048             ]
24049         });
24050         
24051         time.createChild({
24052             tag: 'tr',
24053             cn: [
24054                 {
24055                     tag: 'td',
24056                     cn: [
24057                         {
24058                             tag: 'span',
24059                             cls: 'timepicker-hour',
24060                             html: '00'
24061                         }  
24062                     ]
24063                 },
24064                 {
24065                     tag: 'td',
24066                     cls: 'separator',
24067                     html: ':'
24068                 },
24069                 {
24070                     tag: 'td',
24071                     cn: [
24072                         {
24073                             tag: 'span',
24074                             cls: 'timepicker-minute',
24075                             html: '00'
24076                         }  
24077                     ]
24078                 },
24079                 {
24080                     tag: 'td',
24081                     cls: 'separator'
24082                 },
24083                 {
24084                     tag: 'td',
24085                     cn: [
24086                         {
24087                             tag: 'button',
24088                             type: 'button',
24089                             cls: 'btn btn-primary period',
24090                             html: 'AM'
24091                             
24092                         }
24093                     ]
24094                 }
24095             ]
24096         });
24097         
24098         time.createChild({
24099             tag: 'tr',
24100             cn: [
24101                 {
24102                     tag: 'td',
24103                     cn: [
24104                         {
24105                             tag: 'a',
24106                             href: '#',
24107                             cls: 'btn',
24108                             cn: [
24109                                 {
24110                                     tag: 'span',
24111                                     cls: 'hours-down fa fas fa-chevron-down'
24112                                 }
24113                             ]
24114                         }
24115                     ]
24116                 },
24117                 {
24118                     tag: 'td',
24119                     cls: 'separator'
24120                 },
24121                 {
24122                     tag: 'td',
24123                     cn: [
24124                         {
24125                             tag: 'a',
24126                             href: '#',
24127                             cls: 'btn',
24128                             cn: [
24129                                 {
24130                                     tag: 'span',
24131                                     cls: 'minutes-down fa fas fa-chevron-down'
24132                                 }
24133                             ]
24134                         }
24135                     ]
24136                 },
24137                 {
24138                     tag: 'td',
24139                     cls: 'separator'
24140                 }
24141             ]
24142         });
24143         
24144     },
24145     
24146     update: function()
24147     {
24148         
24149         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24150         
24151         this.fill();
24152     },
24153     
24154     fill: function() 
24155     {
24156         var hours = this.time.getHours();
24157         var minutes = this.time.getMinutes();
24158         var period = 'AM';
24159         
24160         if(hours > 11){
24161             period = 'PM';
24162         }
24163         
24164         if(hours == 0){
24165             hours = 12;
24166         }
24167         
24168         
24169         if(hours > 12){
24170             hours = hours - 12;
24171         }
24172         
24173         if(hours < 10){
24174             hours = '0' + hours;
24175         }
24176         
24177         if(minutes < 10){
24178             minutes = '0' + minutes;
24179         }
24180         
24181         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24182         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24183         this.pop.select('button', true).first().dom.innerHTML = period;
24184         
24185     },
24186     
24187     place: function()
24188     {   
24189         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24190         
24191         var cls = ['bottom'];
24192         
24193         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24194             cls.pop();
24195             cls.push('top');
24196         }
24197         
24198         cls.push('right');
24199         
24200         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24201             cls.pop();
24202             cls.push('left');
24203         }
24204         //this.picker().setXY(20000,20000);
24205         this.picker().addClass(cls.join('-'));
24206         
24207         var _this = this;
24208         
24209         Roo.each(cls, function(c){
24210             if(c == 'bottom'){
24211                 (function() {
24212                  //  
24213                 }).defer(200);
24214                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24215                 //_this.picker().setTop(_this.inputEl().getHeight());
24216                 return;
24217             }
24218             if(c == 'top'){
24219                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24220                 
24221                 //_this.picker().setTop(0 - _this.picker().getHeight());
24222                 return;
24223             }
24224             /*
24225             if(c == 'left'){
24226                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24227                 return;
24228             }
24229             if(c == 'right'){
24230                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24231                 return;
24232             }
24233             */
24234         });
24235         
24236     },
24237   
24238     onFocus : function()
24239     {
24240         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24241         this.show();
24242     },
24243     
24244     onBlur : function()
24245     {
24246         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24247         this.hide();
24248     },
24249     
24250     show : function()
24251     {
24252         this.picker().show();
24253         this.pop.show();
24254         this.update();
24255         this.place();
24256         
24257         this.fireEvent('show', this, this.date);
24258     },
24259     
24260     hide : function()
24261     {
24262         this.picker().hide();
24263         this.pop.hide();
24264         
24265         this.fireEvent('hide', this, this.date);
24266     },
24267     
24268     setTime : function()
24269     {
24270         this.hide();
24271         this.setValue(this.time.format(this.format));
24272         
24273         this.fireEvent('select', this, this.date);
24274         
24275         
24276     },
24277     
24278     onMousedown: function(e){
24279         e.stopPropagation();
24280         e.preventDefault();
24281     },
24282     
24283     onIncrementHours: function()
24284     {
24285         Roo.log('onIncrementHours');
24286         this.time = this.time.add(Date.HOUR, 1);
24287         this.update();
24288         
24289     },
24290     
24291     onDecrementHours: function()
24292     {
24293         Roo.log('onDecrementHours');
24294         this.time = this.time.add(Date.HOUR, -1);
24295         this.update();
24296     },
24297     
24298     onIncrementMinutes: function()
24299     {
24300         Roo.log('onIncrementMinutes');
24301         this.time = this.time.add(Date.MINUTE, 1);
24302         this.update();
24303     },
24304     
24305     onDecrementMinutes: function()
24306     {
24307         Roo.log('onDecrementMinutes');
24308         this.time = this.time.add(Date.MINUTE, -1);
24309         this.update();
24310     },
24311     
24312     onTogglePeriod: function()
24313     {
24314         Roo.log('onTogglePeriod');
24315         this.time = this.time.add(Date.HOUR, 12);
24316         this.update();
24317     }
24318     
24319    
24320 });
24321  
24322
24323 Roo.apply(Roo.bootstrap.form.TimeField,  {
24324   
24325     template : {
24326         tag: 'div',
24327         cls: 'datepicker dropdown-menu',
24328         cn: [
24329             {
24330                 tag: 'div',
24331                 cls: 'datepicker-time',
24332                 cn: [
24333                 {
24334                     tag: 'table',
24335                     cls: 'table-condensed',
24336                     cn:[
24337                         {
24338                             tag: 'tbody',
24339                             cn: [
24340                                 {
24341                                     tag: 'tr',
24342                                     cn: [
24343                                     {
24344                                         tag: 'td',
24345                                         colspan: '7'
24346                                     }
24347                                     ]
24348                                 }
24349                             ]
24350                         },
24351                         {
24352                             tag: 'tfoot',
24353                             cn: [
24354                                 {
24355                                     tag: 'tr',
24356                                     cn: [
24357                                     {
24358                                         tag: 'th',
24359                                         colspan: '7',
24360                                         cls: '',
24361                                         cn: [
24362                                             {
24363                                                 tag: 'button',
24364                                                 cls: 'btn btn-info ok',
24365                                                 html: 'OK'
24366                                             }
24367                                         ]
24368                                     }
24369                     
24370                                     ]
24371                                 }
24372                             ]
24373                         }
24374                     ]
24375                 }
24376                 ]
24377             }
24378         ]
24379     }
24380 });
24381
24382  
24383
24384  /*
24385  * - LGPL
24386  *
24387  * MonthField
24388  * 
24389  */
24390
24391 /**
24392  * @class Roo.bootstrap.form.MonthField
24393  * @extends Roo.bootstrap.form.Input
24394  * Bootstrap MonthField class
24395  * 
24396  * @cfg {String} language default en
24397  * 
24398  * @constructor
24399  * Create a new MonthField
24400  * @param {Object} config The config object
24401  */
24402
24403 Roo.bootstrap.form.MonthField = function(config){
24404     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24405     
24406     this.addEvents({
24407         /**
24408          * @event show
24409          * Fires when this field show.
24410          * @param {Roo.bootstrap.form.MonthField} this
24411          * @param {Mixed} date The date value
24412          */
24413         show : true,
24414         /**
24415          * @event show
24416          * Fires when this field hide.
24417          * @param {Roo.bootstrap.form.MonthField} this
24418          * @param {Mixed} date The date value
24419          */
24420         hide : true,
24421         /**
24422          * @event select
24423          * Fires when select a date.
24424          * @param {Roo.bootstrap.form.MonthField} this
24425          * @param {String} oldvalue The old value
24426          * @param {String} newvalue The new value
24427          */
24428         select : true
24429     });
24430 };
24431
24432 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24433     
24434     onRender: function(ct, position)
24435     {
24436         
24437         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24438         
24439         this.language = this.language || 'en';
24440         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24441         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24442         
24443         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24444         this.isInline = false;
24445         this.isInput = true;
24446         this.component = this.el.select('.add-on', true).first() || false;
24447         this.component = (this.component && this.component.length === 0) ? false : this.component;
24448         this.hasInput = this.component && this.inputEL().length;
24449         
24450         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24451         
24452         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24453         
24454         this.picker().on('mousedown', this.onMousedown, this);
24455         this.picker().on('click', this.onClick, this);
24456         
24457         this.picker().addClass('datepicker-dropdown');
24458         
24459         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24460             v.setStyle('width', '189px');
24461         });
24462         
24463         this.fillMonths();
24464         
24465         this.update();
24466         
24467         if(this.isInline) {
24468             this.show();
24469         }
24470         
24471     },
24472     
24473     setValue: function(v, suppressEvent)
24474     {   
24475         var o = this.getValue();
24476         
24477         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24478         
24479         this.update();
24480
24481         if(suppressEvent !== true){
24482             this.fireEvent('select', this, o, v);
24483         }
24484         
24485     },
24486     
24487     getValue: function()
24488     {
24489         return this.value;
24490     },
24491     
24492     onClick: function(e) 
24493     {
24494         e.stopPropagation();
24495         e.preventDefault();
24496         
24497         var target = e.getTarget();
24498         
24499         if(target.nodeName.toLowerCase() === 'i'){
24500             target = Roo.get(target).dom.parentNode;
24501         }
24502         
24503         var nodeName = target.nodeName;
24504         var className = target.className;
24505         var html = target.innerHTML;
24506         
24507         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24508             return;
24509         }
24510         
24511         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24512         
24513         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24514         
24515         this.hide();
24516                         
24517     },
24518     
24519     picker : function()
24520     {
24521         return this.pickerEl;
24522     },
24523     
24524     fillMonths: function()
24525     {    
24526         var i = 0;
24527         var months = this.picker().select('>.datepicker-months td', true).first();
24528         
24529         months.dom.innerHTML = '';
24530         
24531         while (i < 12) {
24532             var month = {
24533                 tag: 'span',
24534                 cls: 'month',
24535                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24536             };
24537             
24538             months.createChild(month);
24539         }
24540         
24541     },
24542     
24543     update: function()
24544     {
24545         var _this = this;
24546         
24547         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24548             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24549         }
24550         
24551         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24552             e.removeClass('active');
24553             
24554             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24555                 e.addClass('active');
24556             }
24557         })
24558     },
24559     
24560     place: function()
24561     {
24562         if(this.isInline) {
24563             return;
24564         }
24565         
24566         this.picker().removeClass(['bottom', 'top']);
24567         
24568         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24569             /*
24570              * place to the top of element!
24571              *
24572              */
24573             
24574             this.picker().addClass('top');
24575             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24576             
24577             return;
24578         }
24579         
24580         this.picker().addClass('bottom');
24581         
24582         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24583     },
24584     
24585     onFocus : function()
24586     {
24587         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24588         this.show();
24589     },
24590     
24591     onBlur : function()
24592     {
24593         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24594         
24595         var d = this.inputEl().getValue();
24596         
24597         this.setValue(d);
24598                 
24599         this.hide();
24600     },
24601     
24602     show : function()
24603     {
24604         this.picker().show();
24605         this.picker().select('>.datepicker-months', true).first().show();
24606         this.update();
24607         this.place();
24608         
24609         this.fireEvent('show', this, this.date);
24610     },
24611     
24612     hide : function()
24613     {
24614         if(this.isInline) {
24615             return;
24616         }
24617         this.picker().hide();
24618         this.fireEvent('hide', this, this.date);
24619         
24620     },
24621     
24622     onMousedown: function(e)
24623     {
24624         e.stopPropagation();
24625         e.preventDefault();
24626     },
24627     
24628     keyup: function(e)
24629     {
24630         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24631         this.update();
24632     },
24633
24634     fireKey: function(e)
24635     {
24636         if (!this.picker().isVisible()){
24637             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24638                 this.show();
24639             }
24640             return;
24641         }
24642         
24643         var dir;
24644         
24645         switch(e.keyCode){
24646             case 27: // escape
24647                 this.hide();
24648                 e.preventDefault();
24649                 break;
24650             case 37: // left
24651             case 39: // right
24652                 dir = e.keyCode == 37 ? -1 : 1;
24653                 
24654                 this.vIndex = this.vIndex + dir;
24655                 
24656                 if(this.vIndex < 0){
24657                     this.vIndex = 0;
24658                 }
24659                 
24660                 if(this.vIndex > 11){
24661                     this.vIndex = 11;
24662                 }
24663                 
24664                 if(isNaN(this.vIndex)){
24665                     this.vIndex = 0;
24666                 }
24667                 
24668                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24669                 
24670                 break;
24671             case 38: // up
24672             case 40: // down
24673                 
24674                 dir = e.keyCode == 38 ? -1 : 1;
24675                 
24676                 this.vIndex = this.vIndex + dir * 4;
24677                 
24678                 if(this.vIndex < 0){
24679                     this.vIndex = 0;
24680                 }
24681                 
24682                 if(this.vIndex > 11){
24683                     this.vIndex = 11;
24684                 }
24685                 
24686                 if(isNaN(this.vIndex)){
24687                     this.vIndex = 0;
24688                 }
24689                 
24690                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24691                 break;
24692                 
24693             case 13: // enter
24694                 
24695                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24696                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24697                 }
24698                 
24699                 this.hide();
24700                 e.preventDefault();
24701                 break;
24702             case 9: // tab
24703                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24704                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24705                 }
24706                 this.hide();
24707                 break;
24708             case 16: // shift
24709             case 17: // ctrl
24710             case 18: // alt
24711                 break;
24712             default :
24713                 this.hide();
24714                 
24715         }
24716     },
24717     
24718     remove: function() 
24719     {
24720         this.picker().remove();
24721     }
24722    
24723 });
24724
24725 Roo.apply(Roo.bootstrap.form.MonthField,  {
24726     
24727     content : {
24728         tag: 'tbody',
24729         cn: [
24730         {
24731             tag: 'tr',
24732             cn: [
24733             {
24734                 tag: 'td',
24735                 colspan: '7'
24736             }
24737             ]
24738         }
24739         ]
24740     },
24741     
24742     dates:{
24743         en: {
24744             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24745             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24746         }
24747     }
24748 });
24749
24750 Roo.apply(Roo.bootstrap.form.MonthField,  {
24751   
24752     template : {
24753         tag: 'div',
24754         cls: 'datepicker dropdown-menu roo-dynamic',
24755         cn: [
24756             {
24757                 tag: 'div',
24758                 cls: 'datepicker-months',
24759                 cn: [
24760                 {
24761                     tag: 'table',
24762                     cls: 'table-condensed',
24763                     cn:[
24764                         Roo.bootstrap.form.DateField.content
24765                     ]
24766                 }
24767                 ]
24768             }
24769         ]
24770     }
24771 });
24772
24773  
24774
24775  
24776  /*
24777  * - LGPL
24778  *
24779  * CheckBox
24780  * 
24781  */
24782
24783 /**
24784  * @class Roo.bootstrap.form.CheckBox
24785  * @extends Roo.bootstrap.form.Input
24786  * Bootstrap CheckBox class
24787  * 
24788  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24789  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24790  * @cfg {String} boxLabel The text that appears beside the checkbox
24791  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24792  * @cfg {Boolean} checked initnal the element
24793  * @cfg {Boolean} inline inline the element (default false)
24794  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24795  * @cfg {String} tooltip label tooltip
24796  * 
24797  * @constructor
24798  * Create a new CheckBox
24799  * @param {Object} config The config object
24800  */
24801
24802 Roo.bootstrap.form.CheckBox = function(config){
24803     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24804    
24805     this.addEvents({
24806         /**
24807         * @event check
24808         * Fires when the element is checked or unchecked.
24809         * @param {Roo.bootstrap.form.CheckBox} this This input
24810         * @param {Boolean} checked The new checked value
24811         */
24812        check : true,
24813        /**
24814         * @event click
24815         * Fires when the element is click.
24816         * @param {Roo.bootstrap.form.CheckBox} this This input
24817         */
24818        click : true
24819     });
24820     
24821 };
24822
24823 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24824   
24825     inputType: 'checkbox',
24826     inputValue: 1,
24827     valueOff: 0,
24828     boxLabel: false,
24829     checked: false,
24830     weight : false,
24831     inline: false,
24832     tooltip : '',
24833     
24834     // checkbox success does not make any sense really.. 
24835     invalidClass : "",
24836     validClass : "",
24837     
24838     
24839     getAutoCreate : function()
24840     {
24841         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24842         
24843         var id = Roo.id();
24844         
24845         var cfg = {};
24846         
24847         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24848         
24849         if(this.inline){
24850             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24851         }
24852         
24853         var input =  {
24854             tag: 'input',
24855             id : id,
24856             type : this.inputType,
24857             value : this.inputValue,
24858             cls : 'roo-' + this.inputType, //'form-box',
24859             placeholder : this.placeholder || ''
24860             
24861         };
24862         
24863         if(this.inputType != 'radio'){
24864             var hidden =  {
24865                 tag: 'input',
24866                 type : 'hidden',
24867                 cls : 'roo-hidden-value',
24868                 value : this.checked ? this.inputValue : this.valueOff
24869             };
24870         }
24871         
24872             
24873         if (this.weight) { // Validity check?
24874             cfg.cls += " " + this.inputType + "-" + this.weight;
24875         }
24876         
24877         if (this.disabled) {
24878             input.disabled=true;
24879         }
24880         
24881         if(this.checked){
24882             input.checked = this.checked;
24883         }
24884         
24885         if (this.name) {
24886             
24887             input.name = this.name;
24888             
24889             if(this.inputType != 'radio'){
24890                 hidden.name = this.name;
24891                 input.name = '_hidden_' + this.name;
24892             }
24893         }
24894         
24895         if (this.size) {
24896             input.cls += ' input-' + this.size;
24897         }
24898         
24899         var settings=this;
24900         
24901         ['xs','sm','md','lg'].map(function(size){
24902             if (settings[size]) {
24903                 cfg.cls += ' col-' + size + '-' + settings[size];
24904             }
24905         });
24906         
24907         var inputblock = input;
24908          
24909         if (this.before || this.after) {
24910             
24911             inputblock = {
24912                 cls : 'input-group',
24913                 cn :  [] 
24914             };
24915             
24916             if (this.before) {
24917                 inputblock.cn.push({
24918                     tag :'span',
24919                     cls : 'input-group-addon',
24920                     html : this.before
24921                 });
24922             }
24923             
24924             inputblock.cn.push(input);
24925             
24926             if(this.inputType != 'radio'){
24927                 inputblock.cn.push(hidden);
24928             }
24929             
24930             if (this.after) {
24931                 inputblock.cn.push({
24932                     tag :'span',
24933                     cls : 'input-group-addon',
24934                     html : this.after
24935                 });
24936             }
24937             
24938         }
24939         var boxLabelCfg = false;
24940         
24941         if(this.boxLabel){
24942            
24943             boxLabelCfg = {
24944                 tag: 'label',
24945                 //'for': id, // box label is handled by onclick - so no for...
24946                 cls: 'box-label',
24947                 html: this.boxLabel
24948             };
24949             if(this.tooltip){
24950                 boxLabelCfg.tooltip = this.tooltip;
24951             }
24952              
24953         }
24954         
24955         
24956         if (align ==='left' && this.fieldLabel.length) {
24957 //                Roo.log("left and has label");
24958             cfg.cn = [
24959                 {
24960                     tag: 'label',
24961                     'for' :  id,
24962                     cls : 'control-label',
24963                     html : this.fieldLabel
24964                 },
24965                 {
24966                     cls : "", 
24967                     cn: [
24968                         inputblock
24969                     ]
24970                 }
24971             ];
24972             
24973             if (boxLabelCfg) {
24974                 cfg.cn[1].cn.push(boxLabelCfg);
24975             }
24976             
24977             if(this.labelWidth > 12){
24978                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24979             }
24980             
24981             if(this.labelWidth < 13 && this.labelmd == 0){
24982                 this.labelmd = this.labelWidth;
24983             }
24984             
24985             if(this.labellg > 0){
24986                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24987                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24988             }
24989             
24990             if(this.labelmd > 0){
24991                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24992                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24993             }
24994             
24995             if(this.labelsm > 0){
24996                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24997                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24998             }
24999             
25000             if(this.labelxs > 0){
25001                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25002                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25003             }
25004             
25005         } else if ( this.fieldLabel.length) {
25006 //                Roo.log(" label");
25007                 cfg.cn = [
25008                    
25009                     {
25010                         tag: this.boxLabel ? 'span' : 'label',
25011                         'for': id,
25012                         cls: 'control-label box-input-label',
25013                         //cls : 'input-group-addon',
25014                         html : this.fieldLabel
25015                     },
25016                     
25017                     inputblock
25018                     
25019                 ];
25020                 if (boxLabelCfg) {
25021                     cfg.cn.push(boxLabelCfg);
25022                 }
25023
25024         } else {
25025             
25026 //                Roo.log(" no label && no align");
25027                 cfg.cn = [  inputblock ] ;
25028                 if (boxLabelCfg) {
25029                     cfg.cn.push(boxLabelCfg);
25030                 }
25031
25032                 
25033         }
25034         
25035        
25036         
25037         if(this.inputType != 'radio'){
25038             cfg.cn.push(hidden);
25039         }
25040         
25041         return cfg;
25042         
25043     },
25044     
25045     /**
25046      * return the real input element.
25047      */
25048     inputEl: function ()
25049     {
25050         return this.el.select('input.roo-' + this.inputType,true).first();
25051     },
25052     hiddenEl: function ()
25053     {
25054         return this.el.select('input.roo-hidden-value',true).first();
25055     },
25056     
25057     labelEl: function()
25058     {
25059         return this.el.select('label.control-label',true).first();
25060     },
25061     /* depricated... */
25062     
25063     label: function()
25064     {
25065         return this.labelEl();
25066     },
25067     
25068     boxLabelEl: function()
25069     {
25070         return this.el.select('label.box-label',true).first();
25071     },
25072     
25073     initEvents : function()
25074     {
25075 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25076         
25077         this.inputEl().on('click', this.onClick,  this);
25078         
25079         if (this.boxLabel) { 
25080             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25081         }
25082         
25083         this.startValue = this.getValue();
25084         
25085         if(this.groupId){
25086             Roo.bootstrap.form.CheckBox.register(this);
25087         }
25088     },
25089     
25090     onClick : function(e)
25091     {   
25092         if(this.fireEvent('click', this, e) !== false){
25093             this.setChecked(!this.checked);
25094         }
25095         
25096     },
25097     
25098     setChecked : function(state,suppressEvent)
25099     {
25100         this.startValue = this.getValue();
25101
25102         if(this.inputType == 'radio'){
25103             
25104             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25105                 e.dom.checked = false;
25106             });
25107             
25108             this.inputEl().dom.checked = true;
25109             
25110             this.inputEl().dom.value = this.inputValue;
25111             
25112             if(suppressEvent !== true){
25113                 this.fireEvent('check', this, true);
25114             }
25115             
25116             this.validate();
25117             
25118             return;
25119         }
25120         
25121         this.checked = state;
25122         
25123         this.inputEl().dom.checked = state;
25124         
25125         
25126         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25127         
25128         if(suppressEvent !== true){
25129             this.fireEvent('check', this, state);
25130         }
25131         
25132         this.validate();
25133     },
25134     
25135     getValue : function()
25136     {
25137         if(this.inputType == 'radio'){
25138             return this.getGroupValue();
25139         }
25140         
25141         return this.hiddenEl().dom.value;
25142         
25143     },
25144     
25145     getGroupValue : function()
25146     {
25147         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25148             return '';
25149         }
25150         
25151         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25152     },
25153     
25154     setValue : function(v,suppressEvent)
25155     {
25156         if(this.inputType == 'radio'){
25157             this.setGroupValue(v, suppressEvent);
25158             return;
25159         }
25160         
25161         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25162         
25163         this.validate();
25164     },
25165     
25166     setGroupValue : function(v, suppressEvent)
25167     {
25168         this.startValue = this.getValue();
25169         
25170         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25171             e.dom.checked = false;
25172             
25173             if(e.dom.value == v){
25174                 e.dom.checked = true;
25175             }
25176         });
25177         
25178         if(suppressEvent !== true){
25179             this.fireEvent('check', this, true);
25180         }
25181
25182         this.validate();
25183         
25184         return;
25185     },
25186     
25187     validate : function()
25188     {
25189         if(this.getVisibilityEl().hasClass('hidden')){
25190             return true;
25191         }
25192         
25193         if(
25194                 this.disabled || 
25195                 (this.inputType == 'radio' && this.validateRadio()) ||
25196                 (this.inputType == 'checkbox' && this.validateCheckbox())
25197         ){
25198             this.markValid();
25199             return true;
25200         }
25201         
25202         this.markInvalid();
25203         return false;
25204     },
25205     
25206     validateRadio : function()
25207     {
25208         if(this.getVisibilityEl().hasClass('hidden')){
25209             return true;
25210         }
25211         
25212         if(this.allowBlank){
25213             return true;
25214         }
25215         
25216         var valid = false;
25217         
25218         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25219             if(!e.dom.checked){
25220                 return;
25221             }
25222             
25223             valid = true;
25224             
25225             return false;
25226         });
25227         
25228         return valid;
25229     },
25230     
25231     validateCheckbox : function()
25232     {
25233         if(!this.groupId){
25234             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25235             //return (this.getValue() == this.inputValue) ? true : false;
25236         }
25237         
25238         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25239         
25240         if(!group){
25241             return false;
25242         }
25243         
25244         var r = false;
25245         
25246         for(var i in group){
25247             if(group[i].el.isVisible(true)){
25248                 r = false;
25249                 break;
25250             }
25251             
25252             r = true;
25253         }
25254         
25255         for(var i in group){
25256             if(r){
25257                 break;
25258             }
25259             
25260             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25261         }
25262         
25263         return r;
25264     },
25265     
25266     /**
25267      * Mark this field as valid
25268      */
25269     markValid : function()
25270     {
25271         var _this = this;
25272         
25273         this.fireEvent('valid', this);
25274         
25275         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25276         
25277         if(this.groupId){
25278             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25279         }
25280         
25281         if(label){
25282             label.markValid();
25283         }
25284
25285         if(this.inputType == 'radio'){
25286             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25287                 var fg = e.findParent('.form-group', false, true);
25288                 if (Roo.bootstrap.version == 3) {
25289                     fg.removeClass([_this.invalidClass, _this.validClass]);
25290                     fg.addClass(_this.validClass);
25291                 } else {
25292                     fg.removeClass(['is-valid', 'is-invalid']);
25293                     fg.addClass('is-valid');
25294                 }
25295             });
25296             
25297             return;
25298         }
25299
25300         if(!this.groupId){
25301             var fg = this.el.findParent('.form-group', false, true);
25302             if (Roo.bootstrap.version == 3) {
25303                 fg.removeClass([this.invalidClass, this.validClass]);
25304                 fg.addClass(this.validClass);
25305             } else {
25306                 fg.removeClass(['is-valid', 'is-invalid']);
25307                 fg.addClass('is-valid');
25308             }
25309             return;
25310         }
25311         
25312         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25313         
25314         if(!group){
25315             return;
25316         }
25317         
25318         for(var i in group){
25319             var fg = group[i].el.findParent('.form-group', false, true);
25320             if (Roo.bootstrap.version == 3) {
25321                 fg.removeClass([this.invalidClass, this.validClass]);
25322                 fg.addClass(this.validClass);
25323             } else {
25324                 fg.removeClass(['is-valid', 'is-invalid']);
25325                 fg.addClass('is-valid');
25326             }
25327         }
25328     },
25329     
25330      /**
25331      * Mark this field as invalid
25332      * @param {String} msg The validation message
25333      */
25334     markInvalid : function(msg)
25335     {
25336         if(this.allowBlank){
25337             return;
25338         }
25339         
25340         var _this = this;
25341         
25342         this.fireEvent('invalid', this, msg);
25343         
25344         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25345         
25346         if(this.groupId){
25347             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25348         }
25349         
25350         if(label){
25351             label.markInvalid();
25352         }
25353             
25354         if(this.inputType == 'radio'){
25355             
25356             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25357                 var fg = e.findParent('.form-group', false, true);
25358                 if (Roo.bootstrap.version == 3) {
25359                     fg.removeClass([_this.invalidClass, _this.validClass]);
25360                     fg.addClass(_this.invalidClass);
25361                 } else {
25362                     fg.removeClass(['is-invalid', 'is-valid']);
25363                     fg.addClass('is-invalid');
25364                 }
25365             });
25366             
25367             return;
25368         }
25369         
25370         if(!this.groupId){
25371             var fg = this.el.findParent('.form-group', false, true);
25372             if (Roo.bootstrap.version == 3) {
25373                 fg.removeClass([_this.invalidClass, _this.validClass]);
25374                 fg.addClass(_this.invalidClass);
25375             } else {
25376                 fg.removeClass(['is-invalid', 'is-valid']);
25377                 fg.addClass('is-invalid');
25378             }
25379             return;
25380         }
25381         
25382         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25383         
25384         if(!group){
25385             return;
25386         }
25387         
25388         for(var i in group){
25389             var fg = group[i].el.findParent('.form-group', false, true);
25390             if (Roo.bootstrap.version == 3) {
25391                 fg.removeClass([_this.invalidClass, _this.validClass]);
25392                 fg.addClass(_this.invalidClass);
25393             } else {
25394                 fg.removeClass(['is-invalid', 'is-valid']);
25395                 fg.addClass('is-invalid');
25396             }
25397         }
25398         
25399     },
25400     
25401     clearInvalid : function()
25402     {
25403         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25404         
25405         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25406         
25407         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25408         
25409         if (label && label.iconEl) {
25410             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25411             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25412         }
25413     },
25414     
25415     disable : function()
25416     {
25417         if(this.inputType != 'radio'){
25418             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25419             return;
25420         }
25421         
25422         var _this = this;
25423         
25424         if(this.rendered){
25425             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25426                 _this.getActionEl().addClass(this.disabledClass);
25427                 e.dom.disabled = true;
25428             });
25429         }
25430         
25431         this.disabled = true;
25432         this.fireEvent("disable", this);
25433         return this;
25434     },
25435
25436     enable : function()
25437     {
25438         if(this.inputType != 'radio'){
25439             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25440             return;
25441         }
25442         
25443         var _this = this;
25444         
25445         if(this.rendered){
25446             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25447                 _this.getActionEl().removeClass(this.disabledClass);
25448                 e.dom.disabled = false;
25449             });
25450         }
25451         
25452         this.disabled = false;
25453         this.fireEvent("enable", this);
25454         return this;
25455     },
25456     
25457     setBoxLabel : function(v)
25458     {
25459         this.boxLabel = v;
25460         
25461         if(this.rendered){
25462             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25463         }
25464     }
25465
25466 });
25467
25468 Roo.apply(Roo.bootstrap.form.CheckBox, {
25469     
25470     groups: {},
25471     
25472      /**
25473     * register a CheckBox Group
25474     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25475     */
25476     register : function(checkbox)
25477     {
25478         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25479             this.groups[checkbox.groupId] = {};
25480         }
25481         
25482         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25483             return;
25484         }
25485         
25486         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25487         
25488     },
25489     /**
25490     * fetch a CheckBox Group based on the group ID
25491     * @param {string} the group ID
25492     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25493     */
25494     get: function(groupId) {
25495         if (typeof(this.groups[groupId]) == 'undefined') {
25496             return false;
25497         }
25498         
25499         return this.groups[groupId] ;
25500     }
25501     
25502     
25503 });
25504 /*
25505  * - LGPL
25506  *
25507  * RadioItem
25508  * 
25509  */
25510
25511 /**
25512  * @class Roo.bootstrap.form.Radio
25513  * @extends Roo.bootstrap.Component
25514  * Bootstrap Radio class
25515  * @cfg {String} boxLabel - the label associated
25516  * @cfg {String} value - the value of radio
25517  * 
25518  * @constructor
25519  * Create a new Radio
25520  * @param {Object} config The config object
25521  */
25522 Roo.bootstrap.form.Radio = function(config){
25523     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25524     
25525 };
25526
25527 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25528     
25529     boxLabel : '',
25530     
25531     value : '',
25532     
25533     getAutoCreate : function()
25534     {
25535         var cfg = {
25536             tag : 'div',
25537             cls : 'form-group radio',
25538             cn : [
25539                 {
25540                     tag : 'label',
25541                     cls : 'box-label',
25542                     html : this.boxLabel
25543                 }
25544             ]
25545         };
25546         
25547         return cfg;
25548     },
25549     
25550     initEvents : function() 
25551     {
25552         this.parent().register(this);
25553         
25554         this.el.on('click', this.onClick, this);
25555         
25556     },
25557     
25558     onClick : function(e)
25559     {
25560         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25561             this.setChecked(true);
25562         }
25563     },
25564     
25565     setChecked : function(state, suppressEvent)
25566     {
25567         this.parent().setValue(this.value, suppressEvent);
25568         
25569     },
25570     
25571     setBoxLabel : function(v)
25572     {
25573         this.boxLabel = v;
25574         
25575         if(this.rendered){
25576             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25577         }
25578     }
25579     
25580 });
25581  
25582
25583  /*
25584  * - LGPL
25585  *
25586  * Input
25587  * 
25588  */
25589
25590 /**
25591  * @class Roo.bootstrap.form.SecurePass
25592  * @extends Roo.bootstrap.form.Input
25593  * Bootstrap SecurePass class
25594  *
25595  * 
25596  * @constructor
25597  * Create a new SecurePass
25598  * @param {Object} config The config object
25599  */
25600  
25601 Roo.bootstrap.form.SecurePass = function (config) {
25602     // these go here, so the translation tool can replace them..
25603     this.errors = {
25604         PwdEmpty: "Please type a password, and then retype it to confirm.",
25605         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25606         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25607         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25608         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25609         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25610         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25611         TooWeak: "Your password is Too Weak."
25612     },
25613     this.meterLabel = "Password strength:";
25614     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25615     this.meterClass = [
25616         "roo-password-meter-tooweak", 
25617         "roo-password-meter-weak", 
25618         "roo-password-meter-medium", 
25619         "roo-password-meter-strong", 
25620         "roo-password-meter-grey"
25621     ];
25622     
25623     this.errors = {};
25624     
25625     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25626 }
25627
25628 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25629     /**
25630      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25631      * {
25632      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25633      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25634      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25635      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25636      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25637      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25638      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25639      * })
25640      */
25641     // private
25642     
25643     meterWidth: 300,
25644     errorMsg :'',    
25645     errors: false,
25646     imageRoot: '/',
25647     /**
25648      * @cfg {String/Object} Label for the strength meter (defaults to
25649      * 'Password strength:')
25650      */
25651     // private
25652     meterLabel: '',
25653     /**
25654      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25655      * ['Weak', 'Medium', 'Strong'])
25656      */
25657     // private    
25658     pwdStrengths: false,    
25659     // private
25660     strength: 0,
25661     // private
25662     _lastPwd: null,
25663     // private
25664     kCapitalLetter: 0,
25665     kSmallLetter: 1,
25666     kDigit: 2,
25667     kPunctuation: 3,
25668     
25669     insecure: false,
25670     // private
25671     initEvents: function ()
25672     {
25673         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25674
25675         if (this.el.is('input[type=password]') && Roo.isSafari) {
25676             this.el.on('keydown', this.SafariOnKeyDown, this);
25677         }
25678
25679         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25680     },
25681     // private
25682     onRender: function (ct, position)
25683     {
25684         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25685         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25686         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25687
25688         this.trigger.createChild({
25689                    cn: [
25690                     {
25691                     //id: 'PwdMeter',
25692                     tag: 'div',
25693                     cls: 'roo-password-meter-grey col-xs-12',
25694                     style: {
25695                         //width: 0,
25696                         //width: this.meterWidth + 'px'                                                
25697                         }
25698                     },
25699                     {                            
25700                          cls: 'roo-password-meter-text'                          
25701                     }
25702                 ]            
25703         });
25704
25705          
25706         if (this.hideTrigger) {
25707             this.trigger.setDisplayed(false);
25708         }
25709         this.setSize(this.width || '', this.height || '');
25710     },
25711     // private
25712     onDestroy: function ()
25713     {
25714         if (this.trigger) {
25715             this.trigger.removeAllListeners();
25716             this.trigger.remove();
25717         }
25718         if (this.wrap) {
25719             this.wrap.remove();
25720         }
25721         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25722     },
25723     // private
25724     checkStrength: function ()
25725     {
25726         var pwd = this.inputEl().getValue();
25727         if (pwd == this._lastPwd) {
25728             return;
25729         }
25730
25731         var strength;
25732         if (this.ClientSideStrongPassword(pwd)) {
25733             strength = 3;
25734         } else if (this.ClientSideMediumPassword(pwd)) {
25735             strength = 2;
25736         } else if (this.ClientSideWeakPassword(pwd)) {
25737             strength = 1;
25738         } else {
25739             strength = 0;
25740         }
25741         
25742         Roo.log('strength1: ' + strength);
25743         
25744         //var pm = this.trigger.child('div/div/div').dom;
25745         var pm = this.trigger.child('div/div');
25746         pm.removeClass(this.meterClass);
25747         pm.addClass(this.meterClass[strength]);
25748                 
25749         
25750         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25751                 
25752         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25753         
25754         this._lastPwd = pwd;
25755     },
25756     reset: function ()
25757     {
25758         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25759         
25760         this._lastPwd = '';
25761         
25762         var pm = this.trigger.child('div/div');
25763         pm.removeClass(this.meterClass);
25764         pm.addClass('roo-password-meter-grey');        
25765         
25766         
25767         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25768         
25769         pt.innerHTML = '';
25770         this.inputEl().dom.type='password';
25771     },
25772     // private
25773     validateValue: function (value)
25774     {
25775         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25776             return false;
25777         }
25778         if (value.length == 0) {
25779             if (this.allowBlank) {
25780                 this.clearInvalid();
25781                 return true;
25782             }
25783
25784             this.markInvalid(this.errors.PwdEmpty);
25785             this.errorMsg = this.errors.PwdEmpty;
25786             return false;
25787         }
25788         
25789         if(this.insecure){
25790             return true;
25791         }
25792         
25793         if (!value.match(/[\x21-\x7e]+/)) {
25794             this.markInvalid(this.errors.PwdBadChar);
25795             this.errorMsg = this.errors.PwdBadChar;
25796             return false;
25797         }
25798         if (value.length < 6) {
25799             this.markInvalid(this.errors.PwdShort);
25800             this.errorMsg = this.errors.PwdShort;
25801             return false;
25802         }
25803         if (value.length > 16) {
25804             this.markInvalid(this.errors.PwdLong);
25805             this.errorMsg = this.errors.PwdLong;
25806             return false;
25807         }
25808         var strength;
25809         if (this.ClientSideStrongPassword(value)) {
25810             strength = 3;
25811         } else if (this.ClientSideMediumPassword(value)) {
25812             strength = 2;
25813         } else if (this.ClientSideWeakPassword(value)) {
25814             strength = 1;
25815         } else {
25816             strength = 0;
25817         }
25818
25819         
25820         if (strength < 2) {
25821             //this.markInvalid(this.errors.TooWeak);
25822             this.errorMsg = this.errors.TooWeak;
25823             //return false;
25824         }
25825         
25826         
25827         console.log('strength2: ' + strength);
25828         
25829         //var pm = this.trigger.child('div/div/div').dom;
25830         
25831         var pm = this.trigger.child('div/div');
25832         pm.removeClass(this.meterClass);
25833         pm.addClass(this.meterClass[strength]);
25834                 
25835         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25836                 
25837         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25838         
25839         this.errorMsg = ''; 
25840         return true;
25841     },
25842     // private
25843     CharacterSetChecks: function (type)
25844     {
25845         this.type = type;
25846         this.fResult = false;
25847     },
25848     // private
25849     isctype: function (character, type)
25850     {
25851         switch (type) {  
25852             case this.kCapitalLetter:
25853                 if (character >= 'A' && character <= 'Z') {
25854                     return true;
25855                 }
25856                 break;
25857             
25858             case this.kSmallLetter:
25859                 if (character >= 'a' && character <= 'z') {
25860                     return true;
25861                 }
25862                 break;
25863             
25864             case this.kDigit:
25865                 if (character >= '0' && character <= '9') {
25866                     return true;
25867                 }
25868                 break;
25869             
25870             case this.kPunctuation:
25871                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25872                     return true;
25873                 }
25874                 break;
25875             
25876             default:
25877                 return false;
25878         }
25879
25880     },
25881     // private
25882     IsLongEnough: function (pwd, size)
25883     {
25884         return !(pwd == null || isNaN(size) || pwd.length < size);
25885     },
25886     // private
25887     SpansEnoughCharacterSets: function (word, nb)
25888     {
25889         if (!this.IsLongEnough(word, nb))
25890         {
25891             return false;
25892         }
25893
25894         var characterSetChecks = new Array(
25895             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25896             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25897         );
25898         
25899         for (var index = 0; index < word.length; ++index) {
25900             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25901                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25902                     characterSetChecks[nCharSet].fResult = true;
25903                     break;
25904                 }
25905             }
25906         }
25907
25908         var nCharSets = 0;
25909         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25910             if (characterSetChecks[nCharSet].fResult) {
25911                 ++nCharSets;
25912             }
25913         }
25914
25915         if (nCharSets < nb) {
25916             return false;
25917         }
25918         return true;
25919     },
25920     // private
25921     ClientSideStrongPassword: function (pwd)
25922     {
25923         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25924     },
25925     // private
25926     ClientSideMediumPassword: function (pwd)
25927     {
25928         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25929     },
25930     // private
25931     ClientSideWeakPassword: function (pwd)
25932     {
25933         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25934     }
25935           
25936 })//<script type="text/javascript">
25937
25938 /*
25939  * Based  Ext JS Library 1.1.1
25940  * Copyright(c) 2006-2007, Ext JS, LLC.
25941  * LGPL
25942  *
25943  */
25944  
25945 /**
25946  * @class Roo.HtmlEditorCore
25947  * @extends Roo.Component
25948  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25949  *
25950  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25951  */
25952
25953 Roo.HtmlEditorCore = function(config){
25954     
25955     
25956     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25957     
25958     
25959     this.addEvents({
25960         /**
25961          * @event initialize
25962          * Fires when the editor is fully initialized (including the iframe)
25963          * @param {Roo.HtmlEditorCore} this
25964          */
25965         initialize: true,
25966         /**
25967          * @event activate
25968          * Fires when the editor is first receives the focus. Any insertion must wait
25969          * until after this event.
25970          * @param {Roo.HtmlEditorCore} this
25971          */
25972         activate: true,
25973          /**
25974          * @event beforesync
25975          * Fires before the textarea is updated with content from the editor iframe. Return false
25976          * to cancel the sync.
25977          * @param {Roo.HtmlEditorCore} this
25978          * @param {String} html
25979          */
25980         beforesync: true,
25981          /**
25982          * @event beforepush
25983          * Fires before the iframe editor is updated with content from the textarea. Return false
25984          * to cancel the push.
25985          * @param {Roo.HtmlEditorCore} this
25986          * @param {String} html
25987          */
25988         beforepush: true,
25989          /**
25990          * @event sync
25991          * Fires when the textarea is updated with content from the editor iframe.
25992          * @param {Roo.HtmlEditorCore} this
25993          * @param {String} html
25994          */
25995         sync: true,
25996          /**
25997          * @event push
25998          * Fires when the iframe editor is updated with content from the textarea.
25999          * @param {Roo.HtmlEditorCore} this
26000          * @param {String} html
26001          */
26002         push: true,
26003         
26004         /**
26005          * @event editorevent
26006          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26007          * @param {Roo.HtmlEditorCore} this
26008          */
26009         editorevent: true
26010         
26011     });
26012     
26013     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26014     
26015     // defaults : white / black...
26016     this.applyBlacklists();
26017     
26018     
26019     
26020 };
26021
26022
26023 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
26024
26025
26026      /**
26027      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
26028      */
26029     
26030     owner : false,
26031     
26032      /**
26033      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26034      *                        Roo.resizable.
26035      */
26036     resizable : false,
26037      /**
26038      * @cfg {Number} height (in pixels)
26039      */   
26040     height: 300,
26041    /**
26042      * @cfg {Number} width (in pixels)
26043      */   
26044     width: 500,
26045     
26046     /**
26047      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26048      * 
26049      */
26050     stylesheets: false,
26051     
26052     /**
26053      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26054      */
26055     allowComments: false,
26056     // id of frame..
26057     frameId: false,
26058     
26059     // private properties
26060     validationEvent : false,
26061     deferHeight: true,
26062     initialized : false,
26063     activated : false,
26064     sourceEditMode : false,
26065     onFocus : Roo.emptyFn,
26066     iframePad:3,
26067     hideMode:'offsets',
26068     
26069     clearUp: true,
26070     
26071     // blacklist + whitelisted elements..
26072     black: false,
26073     white: false,
26074      
26075     bodyCls : '',
26076
26077     /**
26078      * Protected method that will not generally be called directly. It
26079      * is called when the editor initializes the iframe with HTML contents. Override this method if you
26080      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26081      */
26082     getDocMarkup : function(){
26083         // body styles..
26084         var st = '';
26085         
26086         // inherit styels from page...?? 
26087         if (this.stylesheets === false) {
26088             
26089             Roo.get(document.head).select('style').each(function(node) {
26090                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26091             });
26092             
26093             Roo.get(document.head).select('link').each(function(node) { 
26094                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26095             });
26096             
26097         } else if (!this.stylesheets.length) {
26098                 // simple..
26099                 st = '<style type="text/css">' +
26100                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26101                    '</style>';
26102         } else {
26103             for (var i in this.stylesheets) { 
26104                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26105             }
26106             
26107         }
26108         
26109         st +=  '<style type="text/css">' +
26110             'IMG { cursor: pointer } ' +
26111         '</style>';
26112
26113         var cls = 'roo-htmleditor-body';
26114         
26115         if(this.bodyCls.length){
26116             cls += ' ' + this.bodyCls;
26117         }
26118         
26119         return '<html><head>' + st  +
26120             //<style type="text/css">' +
26121             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26122             //'</style>' +
26123             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
26124     },
26125
26126     // private
26127     onRender : function(ct, position)
26128     {
26129         var _t = this;
26130         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26131         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26132         
26133         
26134         this.el.dom.style.border = '0 none';
26135         this.el.dom.setAttribute('tabIndex', -1);
26136         this.el.addClass('x-hidden hide');
26137         
26138         
26139         
26140         if(Roo.isIE){ // fix IE 1px bogus margin
26141             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26142         }
26143        
26144         
26145         this.frameId = Roo.id();
26146         
26147          
26148         
26149         var iframe = this.owner.wrap.createChild({
26150             tag: 'iframe',
26151             cls: 'form-control', // bootstrap..
26152             id: this.frameId,
26153             name: this.frameId,
26154             frameBorder : 'no',
26155             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
26156         }, this.el
26157         );
26158         
26159         
26160         this.iframe = iframe.dom;
26161
26162          this.assignDocWin();
26163         
26164         this.doc.designMode = 'on';
26165        
26166         this.doc.open();
26167         this.doc.write(this.getDocMarkup());
26168         this.doc.close();
26169
26170         
26171         var task = { // must defer to wait for browser to be ready
26172             run : function(){
26173                 //console.log("run task?" + this.doc.readyState);
26174                 this.assignDocWin();
26175                 if(this.doc.body || this.doc.readyState == 'complete'){
26176                     try {
26177                         this.doc.designMode="on";
26178                     } catch (e) {
26179                         return;
26180                     }
26181                     Roo.TaskMgr.stop(task);
26182                     this.initEditor.defer(10, this);
26183                 }
26184             },
26185             interval : 10,
26186             duration: 10000,
26187             scope: this
26188         };
26189         Roo.TaskMgr.start(task);
26190
26191     },
26192
26193     // private
26194     onResize : function(w, h)
26195     {
26196          Roo.log('resize: ' +w + ',' + h );
26197         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26198         if(!this.iframe){
26199             return;
26200         }
26201         if(typeof w == 'number'){
26202             
26203             this.iframe.style.width = w + 'px';
26204         }
26205         if(typeof h == 'number'){
26206             
26207             this.iframe.style.height = h + 'px';
26208             if(this.doc){
26209                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26210             }
26211         }
26212         
26213     },
26214
26215     /**
26216      * Toggles the editor between standard and source edit mode.
26217      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26218      */
26219     toggleSourceEdit : function(sourceEditMode){
26220         
26221         this.sourceEditMode = sourceEditMode === true;
26222         
26223         if(this.sourceEditMode){
26224  
26225             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
26226             
26227         }else{
26228             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26229             //this.iframe.className = '';
26230             this.deferFocus();
26231         }
26232         //this.setSize(this.owner.wrap.getSize());
26233         //this.fireEvent('editmodechange', this, this.sourceEditMode);
26234     },
26235
26236     
26237   
26238
26239     /**
26240      * Protected method that will not generally be called directly. If you need/want
26241      * custom HTML cleanup, this is the method you should override.
26242      * @param {String} html The HTML to be cleaned
26243      * return {String} The cleaned HTML
26244      */
26245     cleanHtml : function(html){
26246         html = String(html);
26247         if(html.length > 5){
26248             if(Roo.isSafari){ // strip safari nonsense
26249                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26250             }
26251         }
26252         if(html == '&nbsp;'){
26253             html = '';
26254         }
26255         return html;
26256     },
26257
26258     /**
26259      * HTML Editor -> Textarea
26260      * Protected method that will not generally be called directly. Syncs the contents
26261      * of the editor iframe with the textarea.
26262      */
26263     syncValue : function(){
26264         if(this.initialized){
26265             var bd = (this.doc.body || this.doc.documentElement);
26266             //this.cleanUpPaste(); -- this is done else where and causes havoc..
26267             var html = bd.innerHTML;
26268             if(Roo.isSafari){
26269                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26270                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26271                 if(m && m[1]){
26272                     html = '<div style="'+m[0]+'">' + html + '</div>';
26273                 }
26274             }
26275             html = this.cleanHtml(html);
26276             // fix up the special chars.. normaly like back quotes in word...
26277             // however we do not want to do this with chinese..
26278             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26279                 
26280                 var cc = match.charCodeAt();
26281
26282                 // Get the character value, handling surrogate pairs
26283                 if (match.length == 2) {
26284                     // It's a surrogate pair, calculate the Unicode code point
26285                     var high = match.charCodeAt(0) - 0xD800;
26286                     var low  = match.charCodeAt(1) - 0xDC00;
26287                     cc = (high * 0x400) + low + 0x10000;
26288                 }  else if (
26289                     (cc >= 0x4E00 && cc < 0xA000 ) ||
26290                     (cc >= 0x3400 && cc < 0x4E00 ) ||
26291                     (cc >= 0xf900 && cc < 0xfb00 )
26292                 ) {
26293                         return match;
26294                 }  
26295          
26296                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26297                 return "&#" + cc + ";";
26298                 
26299                 
26300             });
26301             
26302             
26303              
26304             if(this.owner.fireEvent('beforesync', this, html) !== false){
26305                 this.el.dom.value = html;
26306                 this.owner.fireEvent('sync', this, html);
26307             }
26308         }
26309     },
26310
26311     /**
26312      * Protected method that will not generally be called directly. Pushes the value of the textarea
26313      * into the iframe editor.
26314      */
26315     pushValue : function(){
26316         if(this.initialized){
26317             var v = this.el.dom.value.trim();
26318             
26319 //            if(v.length < 1){
26320 //                v = '&#160;';
26321 //            }
26322             
26323             if(this.owner.fireEvent('beforepush', this, v) !== false){
26324                 var d = (this.doc.body || this.doc.documentElement);
26325                 d.innerHTML = v;
26326                 this.cleanUpPaste();
26327                 this.el.dom.value = d.innerHTML;
26328                 this.owner.fireEvent('push', this, v);
26329             }
26330         }
26331     },
26332
26333     // private
26334     deferFocus : function(){
26335         this.focus.defer(10, this);
26336     },
26337
26338     // doc'ed in Field
26339     focus : function(){
26340         if(this.win && !this.sourceEditMode){
26341             this.win.focus();
26342         }else{
26343             this.el.focus();
26344         }
26345     },
26346     
26347     assignDocWin: function()
26348     {
26349         var iframe = this.iframe;
26350         
26351          if(Roo.isIE){
26352             this.doc = iframe.contentWindow.document;
26353             this.win = iframe.contentWindow;
26354         } else {
26355 //            if (!Roo.get(this.frameId)) {
26356 //                return;
26357 //            }
26358 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26359 //            this.win = Roo.get(this.frameId).dom.contentWindow;
26360             
26361             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26362                 return;
26363             }
26364             
26365             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26366             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26367         }
26368     },
26369     
26370     // private
26371     initEditor : function(){
26372         //console.log("INIT EDITOR");
26373         this.assignDocWin();
26374         
26375         
26376         
26377         this.doc.designMode="on";
26378         this.doc.open();
26379         this.doc.write(this.getDocMarkup());
26380         this.doc.close();
26381         
26382         var dbody = (this.doc.body || this.doc.documentElement);
26383         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26384         // this copies styles from the containing element into thsi one..
26385         // not sure why we need all of this..
26386         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26387         
26388         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26389         //ss['background-attachment'] = 'fixed'; // w3c
26390         dbody.bgProperties = 'fixed'; // ie
26391         //Roo.DomHelper.applyStyles(dbody, ss);
26392         Roo.EventManager.on(this.doc, {
26393             //'mousedown': this.onEditorEvent,
26394             'mouseup': this.onEditorEvent,
26395             'dblclick': this.onEditorEvent,
26396             'click': this.onEditorEvent,
26397             'keyup': this.onEditorEvent,
26398             buffer:100,
26399             scope: this
26400         });
26401         if(Roo.isGecko){
26402             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26403         }
26404         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26405             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26406         }
26407         this.initialized = true;
26408
26409         this.owner.fireEvent('initialize', this);
26410         this.pushValue();
26411     },
26412
26413     // private
26414     onDestroy : function(){
26415         
26416         
26417         
26418         if(this.rendered){
26419             
26420             //for (var i =0; i < this.toolbars.length;i++) {
26421             //    // fixme - ask toolbars for heights?
26422             //    this.toolbars[i].onDestroy();
26423            // }
26424             
26425             //this.wrap.dom.innerHTML = '';
26426             //this.wrap.remove();
26427         }
26428     },
26429
26430     // private
26431     onFirstFocus : function(){
26432         
26433         this.assignDocWin();
26434         
26435         
26436         this.activated = true;
26437          
26438     
26439         if(Roo.isGecko){ // prevent silly gecko errors
26440             this.win.focus();
26441             var s = this.win.getSelection();
26442             if(!s.focusNode || s.focusNode.nodeType != 3){
26443                 var r = s.getRangeAt(0);
26444                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26445                 r.collapse(true);
26446                 this.deferFocus();
26447             }
26448             try{
26449                 this.execCmd('useCSS', true);
26450                 this.execCmd('styleWithCSS', false);
26451             }catch(e){}
26452         }
26453         this.owner.fireEvent('activate', this);
26454     },
26455
26456     // private
26457     adjustFont: function(btn){
26458         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26459         //if(Roo.isSafari){ // safari
26460         //    adjust *= 2;
26461        // }
26462         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26463         if(Roo.isSafari){ // safari
26464             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26465             v =  (v < 10) ? 10 : v;
26466             v =  (v > 48) ? 48 : v;
26467             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26468             
26469         }
26470         
26471         
26472         v = Math.max(1, v+adjust);
26473         
26474         this.execCmd('FontSize', v  );
26475     },
26476
26477     onEditorEvent : function(e)
26478     {
26479         this.owner.fireEvent('editorevent', this, e);
26480       //  this.updateToolbar();
26481         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26482     },
26483
26484     insertTag : function(tg)
26485     {
26486         // could be a bit smarter... -> wrap the current selected tRoo..
26487         if (tg.toLowerCase() == 'span' ||
26488             tg.toLowerCase() == 'code' ||
26489             tg.toLowerCase() == 'sup' ||
26490             tg.toLowerCase() == 'sub' 
26491             ) {
26492             
26493             range = this.createRange(this.getSelection());
26494             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26495             wrappingNode.appendChild(range.extractContents());
26496             range.insertNode(wrappingNode);
26497
26498             return;
26499             
26500             
26501             
26502         }
26503         this.execCmd("formatblock",   tg);
26504         
26505     },
26506     
26507     insertText : function(txt)
26508     {
26509         
26510         
26511         var range = this.createRange();
26512         range.deleteContents();
26513                //alert(Sender.getAttribute('label'));
26514                
26515         range.insertNode(this.doc.createTextNode(txt));
26516     } ,
26517     
26518      
26519
26520     /**
26521      * Executes a Midas editor command on the editor document and performs necessary focus and
26522      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26523      * @param {String} cmd The Midas command
26524      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26525      */
26526     relayCmd : function(cmd, value){
26527         this.win.focus();
26528         this.execCmd(cmd, value);
26529         this.owner.fireEvent('editorevent', this);
26530         //this.updateToolbar();
26531         this.owner.deferFocus();
26532     },
26533
26534     /**
26535      * Executes a Midas editor command directly on the editor document.
26536      * For visual commands, you should use {@link #relayCmd} instead.
26537      * <b>This should only be called after the editor is initialized.</b>
26538      * @param {String} cmd The Midas command
26539      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26540      */
26541     execCmd : function(cmd, value){
26542         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26543         this.syncValue();
26544     },
26545  
26546  
26547    
26548     /**
26549      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26550      * to insert tRoo.
26551      * @param {String} text | dom node.. 
26552      */
26553     insertAtCursor : function(text)
26554     {
26555         
26556         if(!this.activated){
26557             return;
26558         }
26559         /*
26560         if(Roo.isIE){
26561             this.win.focus();
26562             var r = this.doc.selection.createRange();
26563             if(r){
26564                 r.collapse(true);
26565                 r.pasteHTML(text);
26566                 this.syncValue();
26567                 this.deferFocus();
26568             
26569             }
26570             return;
26571         }
26572         */
26573         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26574             this.win.focus();
26575             
26576             
26577             // from jquery ui (MIT licenced)
26578             var range, node;
26579             var win = this.win;
26580             
26581             if (win.getSelection && win.getSelection().getRangeAt) {
26582                 range = win.getSelection().getRangeAt(0);
26583                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26584                 range.insertNode(node);
26585             } else if (win.document.selection && win.document.selection.createRange) {
26586                 // no firefox support
26587                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26588                 win.document.selection.createRange().pasteHTML(txt);
26589             } else {
26590                 // no firefox support
26591                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26592                 this.execCmd('InsertHTML', txt);
26593             } 
26594             
26595             this.syncValue();
26596             
26597             this.deferFocus();
26598         }
26599     },
26600  // private
26601     mozKeyPress : function(e){
26602         if(e.ctrlKey){
26603             var c = e.getCharCode(), cmd;
26604           
26605             if(c > 0){
26606                 c = String.fromCharCode(c).toLowerCase();
26607                 switch(c){
26608                     case 'b':
26609                         cmd = 'bold';
26610                         break;
26611                     case 'i':
26612                         cmd = 'italic';
26613                         break;
26614                     
26615                     case 'u':
26616                         cmd = 'underline';
26617                         break;
26618                     
26619                     case 'v':
26620                         this.cleanUpPaste.defer(100, this);
26621                         return;
26622                         
26623                 }
26624                 if(cmd){
26625                     this.win.focus();
26626                     this.execCmd(cmd);
26627                     this.deferFocus();
26628                     e.preventDefault();
26629                 }
26630                 
26631             }
26632         }
26633     },
26634
26635     // private
26636     fixKeys : function(){ // load time branching for fastest keydown performance
26637         if(Roo.isIE){
26638             return function(e){
26639                 var k = e.getKey(), r;
26640                 if(k == e.TAB){
26641                     e.stopEvent();
26642                     r = this.doc.selection.createRange();
26643                     if(r){
26644                         r.collapse(true);
26645                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26646                         this.deferFocus();
26647                     }
26648                     return;
26649                 }
26650                 
26651                 if(k == e.ENTER){
26652                     r = this.doc.selection.createRange();
26653                     if(r){
26654                         var target = r.parentElement();
26655                         if(!target || target.tagName.toLowerCase() != 'li'){
26656                             e.stopEvent();
26657                             r.pasteHTML('<br />');
26658                             r.collapse(false);
26659                             r.select();
26660                         }
26661                     }
26662                 }
26663                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26664                     this.cleanUpPaste.defer(100, this);
26665                     return;
26666                 }
26667                 
26668                 
26669             };
26670         }else if(Roo.isOpera){
26671             return function(e){
26672                 var k = e.getKey();
26673                 if(k == e.TAB){
26674                     e.stopEvent();
26675                     this.win.focus();
26676                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26677                     this.deferFocus();
26678                 }
26679                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26680                     this.cleanUpPaste.defer(100, this);
26681                     return;
26682                 }
26683                 
26684             };
26685         }else if(Roo.isSafari){
26686             return function(e){
26687                 var k = e.getKey();
26688                 
26689                 if(k == e.TAB){
26690                     e.stopEvent();
26691                     this.execCmd('InsertText','\t');
26692                     this.deferFocus();
26693                     return;
26694                 }
26695                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26696                     this.cleanUpPaste.defer(100, this);
26697                     return;
26698                 }
26699                 
26700              };
26701         }
26702     }(),
26703     
26704     getAllAncestors: function()
26705     {
26706         var p = this.getSelectedNode();
26707         var a = [];
26708         if (!p) {
26709             a.push(p); // push blank onto stack..
26710             p = this.getParentElement();
26711         }
26712         
26713         
26714         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26715             a.push(p);
26716             p = p.parentNode;
26717         }
26718         a.push(this.doc.body);
26719         return a;
26720     },
26721     lastSel : false,
26722     lastSelNode : false,
26723     
26724     
26725     getSelection : function() 
26726     {
26727         this.assignDocWin();
26728         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26729     },
26730     
26731     getSelectedNode: function() 
26732     {
26733         // this may only work on Gecko!!!
26734         
26735         // should we cache this!!!!
26736         
26737         
26738         
26739          
26740         var range = this.createRange(this.getSelection()).cloneRange();
26741         
26742         if (Roo.isIE) {
26743             var parent = range.parentElement();
26744             while (true) {
26745                 var testRange = range.duplicate();
26746                 testRange.moveToElementText(parent);
26747                 if (testRange.inRange(range)) {
26748                     break;
26749                 }
26750                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26751                     break;
26752                 }
26753                 parent = parent.parentElement;
26754             }
26755             return parent;
26756         }
26757         
26758         // is ancestor a text element.
26759         var ac =  range.commonAncestorContainer;
26760         if (ac.nodeType == 3) {
26761             ac = ac.parentNode;
26762         }
26763         
26764         var ar = ac.childNodes;
26765          
26766         var nodes = [];
26767         var other_nodes = [];
26768         var has_other_nodes = false;
26769         for (var i=0;i<ar.length;i++) {
26770             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26771                 continue;
26772             }
26773             // fullly contained node.
26774             
26775             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26776                 nodes.push(ar[i]);
26777                 continue;
26778             }
26779             
26780             // probably selected..
26781             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26782                 other_nodes.push(ar[i]);
26783                 continue;
26784             }
26785             // outer..
26786             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26787                 continue;
26788             }
26789             
26790             
26791             has_other_nodes = true;
26792         }
26793         if (!nodes.length && other_nodes.length) {
26794             nodes= other_nodes;
26795         }
26796         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26797             return false;
26798         }
26799         
26800         return nodes[0];
26801     },
26802     createRange: function(sel)
26803     {
26804         // this has strange effects when using with 
26805         // top toolbar - not sure if it's a great idea.
26806         //this.editor.contentWindow.focus();
26807         if (typeof sel != "undefined") {
26808             try {
26809                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26810             } catch(e) {
26811                 return this.doc.createRange();
26812             }
26813         } else {
26814             return this.doc.createRange();
26815         }
26816     },
26817     getParentElement: function()
26818     {
26819         
26820         this.assignDocWin();
26821         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26822         
26823         var range = this.createRange(sel);
26824          
26825         try {
26826             var p = range.commonAncestorContainer;
26827             while (p.nodeType == 3) { // text node
26828                 p = p.parentNode;
26829             }
26830             return p;
26831         } catch (e) {
26832             return null;
26833         }
26834     
26835     },
26836     /***
26837      *
26838      * Range intersection.. the hard stuff...
26839      *  '-1' = before
26840      *  '0' = hits..
26841      *  '1' = after.
26842      *         [ -- selected range --- ]
26843      *   [fail]                        [fail]
26844      *
26845      *    basically..
26846      *      if end is before start or  hits it. fail.
26847      *      if start is after end or hits it fail.
26848      *
26849      *   if either hits (but other is outside. - then it's not 
26850      *   
26851      *    
26852      **/
26853     
26854     
26855     // @see http://www.thismuchiknow.co.uk/?p=64.
26856     rangeIntersectsNode : function(range, node)
26857     {
26858         var nodeRange = node.ownerDocument.createRange();
26859         try {
26860             nodeRange.selectNode(node);
26861         } catch (e) {
26862             nodeRange.selectNodeContents(node);
26863         }
26864     
26865         var rangeStartRange = range.cloneRange();
26866         rangeStartRange.collapse(true);
26867     
26868         var rangeEndRange = range.cloneRange();
26869         rangeEndRange.collapse(false);
26870     
26871         var nodeStartRange = nodeRange.cloneRange();
26872         nodeStartRange.collapse(true);
26873     
26874         var nodeEndRange = nodeRange.cloneRange();
26875         nodeEndRange.collapse(false);
26876     
26877         return rangeStartRange.compareBoundaryPoints(
26878                  Range.START_TO_START, nodeEndRange) == -1 &&
26879                rangeEndRange.compareBoundaryPoints(
26880                  Range.START_TO_START, nodeStartRange) == 1;
26881         
26882          
26883     },
26884     rangeCompareNode : function(range, node)
26885     {
26886         var nodeRange = node.ownerDocument.createRange();
26887         try {
26888             nodeRange.selectNode(node);
26889         } catch (e) {
26890             nodeRange.selectNodeContents(node);
26891         }
26892         
26893         
26894         range.collapse(true);
26895     
26896         nodeRange.collapse(true);
26897      
26898         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26899         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26900          
26901         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26902         
26903         var nodeIsBefore   =  ss == 1;
26904         var nodeIsAfter    = ee == -1;
26905         
26906         if (nodeIsBefore && nodeIsAfter) {
26907             return 0; // outer
26908         }
26909         if (!nodeIsBefore && nodeIsAfter) {
26910             return 1; //right trailed.
26911         }
26912         
26913         if (nodeIsBefore && !nodeIsAfter) {
26914             return 2;  // left trailed.
26915         }
26916         // fully contined.
26917         return 3;
26918     },
26919
26920     // private? - in a new class?
26921     cleanUpPaste :  function()
26922     {
26923         // cleans up the whole document..
26924         Roo.log('cleanuppaste');
26925         
26926         this.cleanUpChildren(this.doc.body);
26927         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26928         if (clean != this.doc.body.innerHTML) {
26929             this.doc.body.innerHTML = clean;
26930         }
26931         
26932     },
26933     
26934     cleanWordChars : function(input) {// change the chars to hex code
26935         var he = Roo.HtmlEditorCore;
26936         
26937         var output = input;
26938         Roo.each(he.swapCodes, function(sw) { 
26939             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26940             
26941             output = output.replace(swapper, sw[1]);
26942         });
26943         
26944         return output;
26945     },
26946     
26947     
26948     cleanUpChildren : function (n)
26949     {
26950         if (!n.childNodes.length) {
26951             return;
26952         }
26953         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26954            this.cleanUpChild(n.childNodes[i]);
26955         }
26956     },
26957     
26958     
26959         
26960     
26961     cleanUpChild : function (node)
26962     {
26963         var ed = this;
26964         //console.log(node);
26965         if (node.nodeName == "#text") {
26966             // clean up silly Windows -- stuff?
26967             return; 
26968         }
26969         if (node.nodeName == "#comment") {
26970             if (!this.allowComments) {
26971                 node.parentNode.removeChild(node);
26972             }
26973             // clean up silly Windows -- stuff?
26974             return; 
26975         }
26976         var lcname = node.tagName.toLowerCase();
26977         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26978         // whitelist of tags..
26979         
26980         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26981             // remove node.
26982             node.parentNode.removeChild(node);
26983             return;
26984             
26985         }
26986         
26987         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26988         
26989         // spans with no attributes - just remove them..
26990         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26991             remove_keep_children = true;
26992         }
26993         
26994         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26995         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26996         
26997         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26998         //    remove_keep_children = true;
26999         //}
27000         
27001         if (remove_keep_children) {
27002             this.cleanUpChildren(node);
27003             // inserts everything just before this node...
27004             while (node.childNodes.length) {
27005                 var cn = node.childNodes[0];
27006                 node.removeChild(cn);
27007                 node.parentNode.insertBefore(cn, node);
27008             }
27009             node.parentNode.removeChild(node);
27010             return;
27011         }
27012         
27013         if (!node.attributes || !node.attributes.length) {
27014             
27015           
27016             
27017             
27018             this.cleanUpChildren(node);
27019             return;
27020         }
27021         
27022         function cleanAttr(n,v)
27023         {
27024             
27025             if (v.match(/^\./) || v.match(/^\//)) {
27026                 return;
27027             }
27028             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27029                 return;
27030             }
27031             if (v.match(/^#/)) {
27032                 return;
27033             }
27034             if (v.match(/^\{/)) { // allow template editing.
27035                 return;
27036             }
27037 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27038             node.removeAttribute(n);
27039             
27040         }
27041         
27042         var cwhite = this.cwhite;
27043         var cblack = this.cblack;
27044             
27045         function cleanStyle(n,v)
27046         {
27047             if (v.match(/expression/)) { //XSS?? should we even bother..
27048                 node.removeAttribute(n);
27049                 return;
27050             }
27051             
27052             var parts = v.split(/;/);
27053             var clean = [];
27054             
27055             Roo.each(parts, function(p) {
27056                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27057                 if (!p.length) {
27058                     return true;
27059                 }
27060                 var l = p.split(':').shift().replace(/\s+/g,'');
27061                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27062                 
27063                 if ( cwhite.length && cblack.indexOf(l) > -1) {
27064 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27065                     //node.removeAttribute(n);
27066                     return true;
27067                 }
27068                 //Roo.log()
27069                 // only allow 'c whitelisted system attributes'
27070                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
27071 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27072                     //node.removeAttribute(n);
27073                     return true;
27074                 }
27075                 
27076                 
27077                  
27078                 
27079                 clean.push(p);
27080                 return true;
27081             });
27082             if (clean.length) { 
27083                 node.setAttribute(n, clean.join(';'));
27084             } else {
27085                 node.removeAttribute(n);
27086             }
27087             
27088         }
27089         
27090         
27091         for (var i = node.attributes.length-1; i > -1 ; i--) {
27092             var a = node.attributes[i];
27093             //console.log(a);
27094             
27095             if (a.name.toLowerCase().substr(0,2)=='on')  {
27096                 node.removeAttribute(a.name);
27097                 continue;
27098             }
27099             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27100                 node.removeAttribute(a.name);
27101                 continue;
27102             }
27103             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27104                 cleanAttr(a.name,a.value); // fixme..
27105                 continue;
27106             }
27107             if (a.name == 'style') {
27108                 cleanStyle(a.name,a.value);
27109                 continue;
27110             }
27111             /// clean up MS crap..
27112             // tecnically this should be a list of valid class'es..
27113             
27114             
27115             if (a.name == 'class') {
27116                 if (a.value.match(/^Mso/)) {
27117                     node.removeAttribute('class');
27118                 }
27119                 
27120                 if (a.value.match(/^body$/)) {
27121                     node.removeAttribute('class');
27122                 }
27123                 continue;
27124             }
27125             
27126             // style cleanup!?
27127             // class cleanup?
27128             
27129         }
27130         
27131         
27132         this.cleanUpChildren(node);
27133         
27134         
27135     },
27136     
27137     /**
27138      * Clean up MS wordisms...
27139      */
27140     cleanWord : function(node)
27141     {
27142         if (!node) {
27143             this.cleanWord(this.doc.body);
27144             return;
27145         }
27146         
27147         if(
27148                 node.nodeName == 'SPAN' &&
27149                 !node.hasAttributes() &&
27150                 node.childNodes.length == 1 &&
27151                 node.firstChild.nodeName == "#text"  
27152         ) {
27153             var textNode = node.firstChild;
27154             node.removeChild(textNode);
27155             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27156                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27157             }
27158             node.parentNode.insertBefore(textNode, node);
27159             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27160                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27161             }
27162             node.parentNode.removeChild(node);
27163         }
27164         
27165         if (node.nodeName == "#text") {
27166             // clean up silly Windows -- stuff?
27167             return; 
27168         }
27169         if (node.nodeName == "#comment") {
27170             node.parentNode.removeChild(node);
27171             // clean up silly Windows -- stuff?
27172             return; 
27173         }
27174         
27175         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27176             node.parentNode.removeChild(node);
27177             return;
27178         }
27179         //Roo.log(node.tagName);
27180         // remove - but keep children..
27181         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27182             //Roo.log('-- removed');
27183             while (node.childNodes.length) {
27184                 var cn = node.childNodes[0];
27185                 node.removeChild(cn);
27186                 node.parentNode.insertBefore(cn, node);
27187                 // move node to parent - and clean it..
27188                 this.cleanWord(cn);
27189             }
27190             node.parentNode.removeChild(node);
27191             /// no need to iterate chidlren = it's got none..
27192             //this.iterateChildren(node, this.cleanWord);
27193             return;
27194         }
27195         // clean styles
27196         if (node.className.length) {
27197             
27198             var cn = node.className.split(/\W+/);
27199             var cna = [];
27200             Roo.each(cn, function(cls) {
27201                 if (cls.match(/Mso[a-zA-Z]+/)) {
27202                     return;
27203                 }
27204                 cna.push(cls);
27205             });
27206             node.className = cna.length ? cna.join(' ') : '';
27207             if (!cna.length) {
27208                 node.removeAttribute("class");
27209             }
27210         }
27211         
27212         if (node.hasAttribute("lang")) {
27213             node.removeAttribute("lang");
27214         }
27215         
27216         if (node.hasAttribute("style")) {
27217             
27218             var styles = node.getAttribute("style").split(";");
27219             var nstyle = [];
27220             Roo.each(styles, function(s) {
27221                 if (!s.match(/:/)) {
27222                     return;
27223                 }
27224                 var kv = s.split(":");
27225                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27226                     return;
27227                 }
27228                 // what ever is left... we allow.
27229                 nstyle.push(s);
27230             });
27231             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27232             if (!nstyle.length) {
27233                 node.removeAttribute('style');
27234             }
27235         }
27236         this.iterateChildren(node, this.cleanWord);
27237         
27238         
27239         
27240     },
27241     /**
27242      * iterateChildren of a Node, calling fn each time, using this as the scole..
27243      * @param {DomNode} node node to iterate children of.
27244      * @param {Function} fn method of this class to call on each item.
27245      */
27246     iterateChildren : function(node, fn)
27247     {
27248         if (!node.childNodes.length) {
27249                 return;
27250         }
27251         for (var i = node.childNodes.length-1; i > -1 ; i--) {
27252            fn.call(this, node.childNodes[i])
27253         }
27254     },
27255     
27256     
27257     /**
27258      * cleanTableWidths.
27259      *
27260      * Quite often pasting from word etc.. results in tables with column and widths.
27261      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27262      *
27263      */
27264     cleanTableWidths : function(node)
27265     {
27266          
27267          
27268         if (!node) {
27269             this.cleanTableWidths(this.doc.body);
27270             return;
27271         }
27272         
27273         // ignore list...
27274         if (node.nodeName == "#text" || node.nodeName == "#comment") {
27275             return; 
27276         }
27277         Roo.log(node.tagName);
27278         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27279             this.iterateChildren(node, this.cleanTableWidths);
27280             return;
27281         }
27282         if (node.hasAttribute('width')) {
27283             node.removeAttribute('width');
27284         }
27285         
27286          
27287         if (node.hasAttribute("style")) {
27288             // pretty basic...
27289             
27290             var styles = node.getAttribute("style").split(";");
27291             var nstyle = [];
27292             Roo.each(styles, function(s) {
27293                 if (!s.match(/:/)) {
27294                     return;
27295                 }
27296                 var kv = s.split(":");
27297                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27298                     return;
27299                 }
27300                 // what ever is left... we allow.
27301                 nstyle.push(s);
27302             });
27303             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27304             if (!nstyle.length) {
27305                 node.removeAttribute('style');
27306             }
27307         }
27308         
27309         this.iterateChildren(node, this.cleanTableWidths);
27310         
27311         
27312     },
27313     
27314     
27315     
27316     
27317     domToHTML : function(currentElement, depth, nopadtext) {
27318         
27319         depth = depth || 0;
27320         nopadtext = nopadtext || false;
27321     
27322         if (!currentElement) {
27323             return this.domToHTML(this.doc.body);
27324         }
27325         
27326         //Roo.log(currentElement);
27327         var j;
27328         var allText = false;
27329         var nodeName = currentElement.nodeName;
27330         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27331         
27332         if  (nodeName == '#text') {
27333             
27334             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27335         }
27336         
27337         
27338         var ret = '';
27339         if (nodeName != 'BODY') {
27340              
27341             var i = 0;
27342             // Prints the node tagName, such as <A>, <IMG>, etc
27343             if (tagName) {
27344                 var attr = [];
27345                 for(i = 0; i < currentElement.attributes.length;i++) {
27346                     // quoting?
27347                     var aname = currentElement.attributes.item(i).name;
27348                     if (!currentElement.attributes.item(i).value.length) {
27349                         continue;
27350                     }
27351                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27352                 }
27353                 
27354                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27355             } 
27356             else {
27357                 
27358                 // eack
27359             }
27360         } else {
27361             tagName = false;
27362         }
27363         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27364             return ret;
27365         }
27366         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27367             nopadtext = true;
27368         }
27369         
27370         
27371         // Traverse the tree
27372         i = 0;
27373         var currentElementChild = currentElement.childNodes.item(i);
27374         var allText = true;
27375         var innerHTML  = '';
27376         lastnode = '';
27377         while (currentElementChild) {
27378             // Formatting code (indent the tree so it looks nice on the screen)
27379             var nopad = nopadtext;
27380             if (lastnode == 'SPAN') {
27381                 nopad  = true;
27382             }
27383             // text
27384             if  (currentElementChild.nodeName == '#text') {
27385                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27386                 toadd = nopadtext ? toadd : toadd.trim();
27387                 if (!nopad && toadd.length > 80) {
27388                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
27389                 }
27390                 innerHTML  += toadd;
27391                 
27392                 i++;
27393                 currentElementChild = currentElement.childNodes.item(i);
27394                 lastNode = '';
27395                 continue;
27396             }
27397             allText = false;
27398             
27399             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
27400                 
27401             // Recursively traverse the tree structure of the child node
27402             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
27403             lastnode = currentElementChild.nodeName;
27404             i++;
27405             currentElementChild=currentElement.childNodes.item(i);
27406         }
27407         
27408         ret += innerHTML;
27409         
27410         if (!allText) {
27411                 // The remaining code is mostly for formatting the tree
27412             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27413         }
27414         
27415         
27416         if (tagName) {
27417             ret+= "</"+tagName+">";
27418         }
27419         return ret;
27420         
27421     },
27422         
27423     applyBlacklists : function()
27424     {
27425         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27426         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27427         
27428         this.white = [];
27429         this.black = [];
27430         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27431             if (b.indexOf(tag) > -1) {
27432                 return;
27433             }
27434             this.white.push(tag);
27435             
27436         }, this);
27437         
27438         Roo.each(w, function(tag) {
27439             if (b.indexOf(tag) > -1) {
27440                 return;
27441             }
27442             if (this.white.indexOf(tag) > -1) {
27443                 return;
27444             }
27445             this.white.push(tag);
27446             
27447         }, this);
27448         
27449         
27450         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27451             if (w.indexOf(tag) > -1) {
27452                 return;
27453             }
27454             this.black.push(tag);
27455             
27456         }, this);
27457         
27458         Roo.each(b, function(tag) {
27459             if (w.indexOf(tag) > -1) {
27460                 return;
27461             }
27462             if (this.black.indexOf(tag) > -1) {
27463                 return;
27464             }
27465             this.black.push(tag);
27466             
27467         }, this);
27468         
27469         
27470         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27471         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27472         
27473         this.cwhite = [];
27474         this.cblack = [];
27475         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27476             if (b.indexOf(tag) > -1) {
27477                 return;
27478             }
27479             this.cwhite.push(tag);
27480             
27481         }, this);
27482         
27483         Roo.each(w, function(tag) {
27484             if (b.indexOf(tag) > -1) {
27485                 return;
27486             }
27487             if (this.cwhite.indexOf(tag) > -1) {
27488                 return;
27489             }
27490             this.cwhite.push(tag);
27491             
27492         }, this);
27493         
27494         
27495         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27496             if (w.indexOf(tag) > -1) {
27497                 return;
27498             }
27499             this.cblack.push(tag);
27500             
27501         }, this);
27502         
27503         Roo.each(b, function(tag) {
27504             if (w.indexOf(tag) > -1) {
27505                 return;
27506             }
27507             if (this.cblack.indexOf(tag) > -1) {
27508                 return;
27509             }
27510             this.cblack.push(tag);
27511             
27512         }, this);
27513     },
27514     
27515     setStylesheets : function(stylesheets)
27516     {
27517         if(typeof(stylesheets) == 'string'){
27518             Roo.get(this.iframe.contentDocument.head).createChild({
27519                 tag : 'link',
27520                 rel : 'stylesheet',
27521                 type : 'text/css',
27522                 href : stylesheets
27523             });
27524             
27525             return;
27526         }
27527         var _this = this;
27528      
27529         Roo.each(stylesheets, function(s) {
27530             if(!s.length){
27531                 return;
27532             }
27533             
27534             Roo.get(_this.iframe.contentDocument.head).createChild({
27535                 tag : 'link',
27536                 rel : 'stylesheet',
27537                 type : 'text/css',
27538                 href : s
27539             });
27540         });
27541
27542         
27543     },
27544     
27545     removeStylesheets : function()
27546     {
27547         var _this = this;
27548         
27549         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27550             s.remove();
27551         });
27552     },
27553     
27554     setStyle : function(style)
27555     {
27556         Roo.get(this.iframe.contentDocument.head).createChild({
27557             tag : 'style',
27558             type : 'text/css',
27559             html : style
27560         });
27561
27562         return;
27563     }
27564     
27565     // hide stuff that is not compatible
27566     /**
27567      * @event blur
27568      * @hide
27569      */
27570     /**
27571      * @event change
27572      * @hide
27573      */
27574     /**
27575      * @event focus
27576      * @hide
27577      */
27578     /**
27579      * @event specialkey
27580      * @hide
27581      */
27582     /**
27583      * @cfg {String} fieldClass @hide
27584      */
27585     /**
27586      * @cfg {String} focusClass @hide
27587      */
27588     /**
27589      * @cfg {String} autoCreate @hide
27590      */
27591     /**
27592      * @cfg {String} inputType @hide
27593      */
27594     /**
27595      * @cfg {String} invalidClass @hide
27596      */
27597     /**
27598      * @cfg {String} invalidText @hide
27599      */
27600     /**
27601      * @cfg {String} msgFx @hide
27602      */
27603     /**
27604      * @cfg {String} validateOnBlur @hide
27605      */
27606 });
27607
27608 Roo.HtmlEditorCore.white = [
27609         'area', 'br', 'img', 'input', 'hr', 'wbr',
27610         
27611        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27612        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27613        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27614        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27615        'table',   'ul',         'xmp', 
27616        
27617        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27618       'thead',   'tr', 
27619      
27620       'dir', 'menu', 'ol', 'ul', 'dl',
27621        
27622       'embed',  'object'
27623 ];
27624
27625
27626 Roo.HtmlEditorCore.black = [
27627     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27628         'applet', // 
27629         'base',   'basefont', 'bgsound', 'blink',  'body', 
27630         'frame',  'frameset', 'head',    'html',   'ilayer', 
27631         'iframe', 'layer',  'link',     'meta',    'object',   
27632         'script', 'style' ,'title',  'xml' // clean later..
27633 ];
27634 Roo.HtmlEditorCore.clean = [
27635     'script', 'style', 'title', 'xml'
27636 ];
27637 Roo.HtmlEditorCore.remove = [
27638     'font'
27639 ];
27640 // attributes..
27641
27642 Roo.HtmlEditorCore.ablack = [
27643     'on'
27644 ];
27645     
27646 Roo.HtmlEditorCore.aclean = [ 
27647     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27648 ];
27649
27650 // protocols..
27651 Roo.HtmlEditorCore.pwhite= [
27652         'http',  'https',  'mailto'
27653 ];
27654
27655 // white listed style attributes.
27656 Roo.HtmlEditorCore.cwhite= [
27657       //  'text-align', /// default is to allow most things..
27658       
27659          
27660 //        'font-size'//??
27661 ];
27662
27663 // black listed style attributes.
27664 Roo.HtmlEditorCore.cblack= [
27665       //  'font-size' -- this can be set by the project 
27666 ];
27667
27668
27669 Roo.HtmlEditorCore.swapCodes   =[ 
27670     [    8211, "&#8211;" ], 
27671     [    8212, "&#8212;" ], 
27672     [    8216,  "'" ],  
27673     [    8217, "'" ],  
27674     [    8220, '"' ],  
27675     [    8221, '"' ],  
27676     [    8226, "*" ],  
27677     [    8230, "..." ]
27678 ]; 
27679
27680     /*
27681  * - LGPL
27682  *
27683  * HtmlEditor
27684  * 
27685  */
27686
27687 /**
27688  * @class Roo.bootstrap.form.HtmlEditor
27689  * @extends Roo.bootstrap.form.TextArea
27690  * Bootstrap HtmlEditor class
27691
27692  * @constructor
27693  * Create a new HtmlEditor
27694  * @param {Object} config The config object
27695  */
27696
27697 Roo.bootstrap.form.HtmlEditor = function(config){
27698     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27699     if (!this.toolbars) {
27700         this.toolbars = [];
27701     }
27702     
27703     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27704     this.addEvents({
27705             /**
27706              * @event initialize
27707              * Fires when the editor is fully initialized (including the iframe)
27708              * @param {HtmlEditor} this
27709              */
27710             initialize: true,
27711             /**
27712              * @event activate
27713              * Fires when the editor is first receives the focus. Any insertion must wait
27714              * until after this event.
27715              * @param {HtmlEditor} this
27716              */
27717             activate: true,
27718              /**
27719              * @event beforesync
27720              * Fires before the textarea is updated with content from the editor iframe. Return false
27721              * to cancel the sync.
27722              * @param {HtmlEditor} this
27723              * @param {String} html
27724              */
27725             beforesync: true,
27726              /**
27727              * @event beforepush
27728              * Fires before the iframe editor is updated with content from the textarea. Return false
27729              * to cancel the push.
27730              * @param {HtmlEditor} this
27731              * @param {String} html
27732              */
27733             beforepush: true,
27734              /**
27735              * @event sync
27736              * Fires when the textarea is updated with content from the editor iframe.
27737              * @param {HtmlEditor} this
27738              * @param {String} html
27739              */
27740             sync: true,
27741              /**
27742              * @event push
27743              * Fires when the iframe editor is updated with content from the textarea.
27744              * @param {HtmlEditor} this
27745              * @param {String} html
27746              */
27747             push: true,
27748              /**
27749              * @event editmodechange
27750              * Fires when the editor switches edit modes
27751              * @param {HtmlEditor} this
27752              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27753              */
27754             editmodechange: true,
27755             /**
27756              * @event editorevent
27757              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27758              * @param {HtmlEditor} this
27759              */
27760             editorevent: true,
27761             /**
27762              * @event firstfocus
27763              * Fires when on first focus - needed by toolbars..
27764              * @param {HtmlEditor} this
27765              */
27766             firstfocus: true,
27767             /**
27768              * @event autosave
27769              * Auto save the htmlEditor value as a file into Events
27770              * @param {HtmlEditor} this
27771              */
27772             autosave: true,
27773             /**
27774              * @event savedpreview
27775              * preview the saved version of htmlEditor
27776              * @param {HtmlEditor} this
27777              */
27778             savedpreview: true
27779         });
27780 };
27781
27782
27783 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
27784     
27785     
27786       /**
27787      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27788      */
27789     toolbars : false,
27790     
27791      /**
27792     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27793     */
27794     btns : [],
27795    
27796      /**
27797      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27798      *                        Roo.resizable.
27799      */
27800     resizable : false,
27801      /**
27802      * @cfg {Number} height (in pixels)
27803      */   
27804     height: 300,
27805    /**
27806      * @cfg {Number} width (in pixels)
27807      */   
27808     width: false,
27809     
27810     /**
27811      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27812      * 
27813      */
27814     stylesheets: false,
27815     
27816     // id of frame..
27817     frameId: false,
27818     
27819     // private properties
27820     validationEvent : false,
27821     deferHeight: true,
27822     initialized : false,
27823     activated : false,
27824     
27825     onFocus : Roo.emptyFn,
27826     iframePad:3,
27827     hideMode:'offsets',
27828     
27829     tbContainer : false,
27830     
27831     bodyCls : '',
27832     
27833     toolbarContainer :function() {
27834         return this.wrap.select('.x-html-editor-tb',true).first();
27835     },
27836
27837     /**
27838      * Protected method that will not generally be called directly. It
27839      * is called when the editor creates its toolbar. Override this method if you need to
27840      * add custom toolbar buttons.
27841      * @param {HtmlEditor} editor
27842      */
27843     createToolbar : function(){
27844         Roo.log('renewing');
27845         Roo.log("create toolbars");
27846         
27847         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27848         this.toolbars[0].render(this.toolbarContainer());
27849         
27850         return;
27851         
27852 //        if (!editor.toolbars || !editor.toolbars.length) {
27853 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27854 //        }
27855 //        
27856 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27857 //            editor.toolbars[i] = Roo.factory(
27858 //                    typeof(editor.toolbars[i]) == 'string' ?
27859 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27860 //                Roo.bootstrap.form.HtmlEditor);
27861 //            editor.toolbars[i].init(editor);
27862 //        }
27863     },
27864
27865      
27866     // private
27867     onRender : function(ct, position)
27868     {
27869        // Roo.log("Call onRender: " + this.xtype);
27870         var _t = this;
27871         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27872       
27873         this.wrap = this.inputEl().wrap({
27874             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27875         });
27876         
27877         this.editorcore.onRender(ct, position);
27878          
27879         if (this.resizable) {
27880             this.resizeEl = new Roo.Resizable(this.wrap, {
27881                 pinned : true,
27882                 wrap: true,
27883                 dynamic : true,
27884                 minHeight : this.height,
27885                 height: this.height,
27886                 handles : this.resizable,
27887                 width: this.width,
27888                 listeners : {
27889                     resize : function(r, w, h) {
27890                         _t.onResize(w,h); // -something
27891                     }
27892                 }
27893             });
27894             
27895         }
27896         this.createToolbar(this);
27897        
27898         
27899         if(!this.width && this.resizable){
27900             this.setSize(this.wrap.getSize());
27901         }
27902         if (this.resizeEl) {
27903             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27904             // should trigger onReize..
27905         }
27906         
27907     },
27908
27909     // private
27910     onResize : function(w, h)
27911     {
27912         Roo.log('resize: ' +w + ',' + h );
27913         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27914         var ew = false;
27915         var eh = false;
27916         
27917         if(this.inputEl() ){
27918             if(typeof w == 'number'){
27919                 var aw = w - this.wrap.getFrameWidth('lr');
27920                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27921                 ew = aw;
27922             }
27923             if(typeof h == 'number'){
27924                  var tbh = -11;  // fixme it needs to tool bar size!
27925                 for (var i =0; i < this.toolbars.length;i++) {
27926                     // fixme - ask toolbars for heights?
27927                     tbh += this.toolbars[i].el.getHeight();
27928                     //if (this.toolbars[i].footer) {
27929                     //    tbh += this.toolbars[i].footer.el.getHeight();
27930                     //}
27931                 }
27932               
27933                 
27934                 
27935                 
27936                 
27937                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27938                 ah -= 5; // knock a few pixes off for look..
27939                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27940                 var eh = ah;
27941             }
27942         }
27943         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27944         this.editorcore.onResize(ew,eh);
27945         
27946     },
27947
27948     /**
27949      * Toggles the editor between standard and source edit mode.
27950      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27951      */
27952     toggleSourceEdit : function(sourceEditMode)
27953     {
27954         this.editorcore.toggleSourceEdit(sourceEditMode);
27955         
27956         if(this.editorcore.sourceEditMode){
27957             Roo.log('editor - showing textarea');
27958             
27959 //            Roo.log('in');
27960 //            Roo.log(this.syncValue());
27961             this.syncValue();
27962             this.inputEl().removeClass(['hide', 'x-hidden']);
27963             this.inputEl().dom.removeAttribute('tabIndex');
27964             this.inputEl().focus();
27965         }else{
27966             Roo.log('editor - hiding textarea');
27967 //            Roo.log('out')
27968 //            Roo.log(this.pushValue()); 
27969             this.pushValue();
27970             
27971             this.inputEl().addClass(['hide', 'x-hidden']);
27972             this.inputEl().dom.setAttribute('tabIndex', -1);
27973             //this.deferFocus();
27974         }
27975          
27976         if(this.resizable){
27977             this.setSize(this.wrap.getSize());
27978         }
27979         
27980         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27981     },
27982  
27983     // private (for BoxComponent)
27984     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27985
27986     // private (for BoxComponent)
27987     getResizeEl : function(){
27988         return this.wrap;
27989     },
27990
27991     // private (for BoxComponent)
27992     getPositionEl : function(){
27993         return this.wrap;
27994     },
27995
27996     // private
27997     initEvents : function(){
27998         this.originalValue = this.getValue();
27999     },
28000
28001 //    /**
28002 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28003 //     * @method
28004 //     */
28005 //    markInvalid : Roo.emptyFn,
28006 //    /**
28007 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28008 //     * @method
28009 //     */
28010 //    clearInvalid : Roo.emptyFn,
28011
28012     setValue : function(v){
28013         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28014         this.editorcore.pushValue();
28015     },
28016
28017      
28018     // private
28019     deferFocus : function(){
28020         this.focus.defer(10, this);
28021     },
28022
28023     // doc'ed in Field
28024     focus : function(){
28025         this.editorcore.focus();
28026         
28027     },
28028       
28029
28030     // private
28031     onDestroy : function(){
28032         
28033         
28034         
28035         if(this.rendered){
28036             
28037             for (var i =0; i < this.toolbars.length;i++) {
28038                 // fixme - ask toolbars for heights?
28039                 this.toolbars[i].onDestroy();
28040             }
28041             
28042             this.wrap.dom.innerHTML = '';
28043             this.wrap.remove();
28044         }
28045     },
28046
28047     // private
28048     onFirstFocus : function(){
28049         //Roo.log("onFirstFocus");
28050         this.editorcore.onFirstFocus();
28051          for (var i =0; i < this.toolbars.length;i++) {
28052             this.toolbars[i].onFirstFocus();
28053         }
28054         
28055     },
28056     
28057     // private
28058     syncValue : function()
28059     {   
28060         this.editorcore.syncValue();
28061     },
28062     
28063     pushValue : function()
28064     {   
28065         this.editorcore.pushValue();
28066     }
28067      
28068     
28069     // hide stuff that is not compatible
28070     /**
28071      * @event blur
28072      * @hide
28073      */
28074     /**
28075      * @event change
28076      * @hide
28077      */
28078     /**
28079      * @event focus
28080      * @hide
28081      */
28082     /**
28083      * @event specialkey
28084      * @hide
28085      */
28086     /**
28087      * @cfg {String} fieldClass @hide
28088      */
28089     /**
28090      * @cfg {String} focusClass @hide
28091      */
28092     /**
28093      * @cfg {String} autoCreate @hide
28094      */
28095     /**
28096      * @cfg {String} inputType @hide
28097      */
28098      
28099     /**
28100      * @cfg {String} invalidText @hide
28101      */
28102     /**
28103      * @cfg {String} msgFx @hide
28104      */
28105     /**
28106      * @cfg {String} validateOnBlur @hide
28107      */
28108 });
28109  
28110     
28111    
28112    
28113    
28114       
28115 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28116 /**
28117  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28118  * @parent Roo.bootstrap.form.HtmlEditor
28119  * @extends Roo.bootstrap.nav.Simplebar
28120  * Basic Toolbar
28121  * 
28122  * @example
28123  * Usage:
28124  *
28125  new Roo.bootstrap.form.HtmlEditor({
28126     ....
28127     toolbars : [
28128         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28129             disable : { fonts: 1 , format: 1, ..., ... , ...],
28130             btns : [ .... ]
28131         })
28132     }
28133      
28134  * 
28135  * @cfg {Object} disable List of elements to disable..
28136  * @cfg {Array} btns List of additional buttons.
28137  * 
28138  * 
28139  * NEEDS Extra CSS? 
28140  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28141  */
28142  
28143 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28144 {
28145     
28146     Roo.apply(this, config);
28147     
28148     // default disabled, based on 'good practice'..
28149     this.disable = this.disable || {};
28150     Roo.applyIf(this.disable, {
28151         fontSize : true,
28152         colors : true,
28153         specialElements : true
28154     });
28155     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28156     
28157     this.editor = config.editor;
28158     this.editorcore = config.editor.editorcore;
28159     
28160     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28161     
28162     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28163     // dont call parent... till later.
28164 }
28165 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
28166      
28167     bar : true,
28168     
28169     editor : false,
28170     editorcore : false,
28171     
28172     
28173     formats : [
28174         "p" ,  
28175         "h1","h2","h3","h4","h5","h6", 
28176         "pre", "code", 
28177         "abbr", "acronym", "address", "cite", "samp", "var",
28178         'div','span'
28179     ],
28180     
28181     onRender : function(ct, position)
28182     {
28183        // Roo.log("Call onRender: " + this.xtype);
28184         
28185        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28186        Roo.log(this.el);
28187        this.el.dom.style.marginBottom = '0';
28188        var _this = this;
28189        var editorcore = this.editorcore;
28190        var editor= this.editor;
28191        
28192        var children = [];
28193        var btn = function(id,cmd , toggle, handler, html){
28194        
28195             var  event = toggle ? 'toggle' : 'click';
28196        
28197             var a = {
28198                 size : 'sm',
28199                 xtype: 'Button',
28200                 xns: Roo.bootstrap,
28201                 //glyphicon : id,
28202                 fa: id,
28203                 cmd : id || cmd,
28204                 enableToggle:toggle !== false,
28205                 html : html || '',
28206                 pressed : toggle ? false : null,
28207                 listeners : {}
28208             };
28209             a.listeners[toggle ? 'toggle' : 'click'] = function() {
28210                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
28211             };
28212             children.push(a);
28213             return a;
28214        }
28215        
28216     //    var cb_box = function...
28217         
28218         var style = {
28219                 xtype: 'Button',
28220                 size : 'sm',
28221                 xns: Roo.bootstrap,
28222                 fa : 'font',
28223                 //html : 'submit'
28224                 menu : {
28225                     xtype: 'Menu',
28226                     xns: Roo.bootstrap,
28227                     items:  []
28228                 }
28229         };
28230         Roo.each(this.formats, function(f) {
28231             style.menu.items.push({
28232                 xtype :'MenuItem',
28233                 xns: Roo.bootstrap,
28234                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28235                 tagname : f,
28236                 listeners : {
28237                     click : function()
28238                     {
28239                         editorcore.insertTag(this.tagname);
28240                         editor.focus();
28241                     }
28242                 }
28243                 
28244             });
28245         });
28246         children.push(style);   
28247         
28248         btn('bold',false,true);
28249         btn('italic',false,true);
28250         btn('align-left', 'justifyleft',true);
28251         btn('align-center', 'justifycenter',true);
28252         btn('align-right' , 'justifyright',true);
28253         btn('link', false, false, function(btn) {
28254             //Roo.log("create link?");
28255             var url = prompt(this.createLinkText, this.defaultLinkValue);
28256             if(url && url != 'http:/'+'/'){
28257                 this.editorcore.relayCmd('createlink', url);
28258             }
28259         }),
28260         btn('list','insertunorderedlist',true);
28261         btn('pencil', false,true, function(btn){
28262                 Roo.log(this);
28263                 this.toggleSourceEdit(btn.pressed);
28264         });
28265         
28266         if (this.editor.btns.length > 0) {
28267             for (var i = 0; i<this.editor.btns.length; i++) {
28268                 children.push(this.editor.btns[i]);
28269             }
28270         }
28271         
28272         /*
28273         var cog = {
28274                 xtype: 'Button',
28275                 size : 'sm',
28276                 xns: Roo.bootstrap,
28277                 glyphicon : 'cog',
28278                 //html : 'submit'
28279                 menu : {
28280                     xtype: 'Menu',
28281                     xns: Roo.bootstrap,
28282                     items:  []
28283                 }
28284         };
28285         
28286         cog.menu.items.push({
28287             xtype :'MenuItem',
28288             xns: Roo.bootstrap,
28289             html : Clean styles,
28290             tagname : f,
28291             listeners : {
28292                 click : function()
28293                 {
28294                     editorcore.insertTag(this.tagname);
28295                     editor.focus();
28296                 }
28297             }
28298             
28299         });
28300        */
28301         
28302          
28303        this.xtype = 'NavSimplebar';
28304         
28305         for(var i=0;i< children.length;i++) {
28306             
28307             this.buttons.add(this.addxtypeChild(children[i]));
28308             
28309         }
28310         
28311         editor.on('editorevent', this.updateToolbar, this);
28312     },
28313     onBtnClick : function(id)
28314     {
28315        this.editorcore.relayCmd(id);
28316        this.editorcore.focus();
28317     },
28318     
28319     /**
28320      * Protected method that will not generally be called directly. It triggers
28321      * a toolbar update by reading the markup state of the current selection in the editor.
28322      */
28323     updateToolbar: function(){
28324
28325         if(!this.editorcore.activated){
28326             this.editor.onFirstFocus(); // is this neeed?
28327             return;
28328         }
28329
28330         var btns = this.buttons; 
28331         var doc = this.editorcore.doc;
28332         btns.get('bold').setActive(doc.queryCommandState('bold'));
28333         btns.get('italic').setActive(doc.queryCommandState('italic'));
28334         //btns.get('underline').setActive(doc.queryCommandState('underline'));
28335         
28336         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28337         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28338         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28339         
28340         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28341         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28342          /*
28343         
28344         var ans = this.editorcore.getAllAncestors();
28345         if (this.formatCombo) {
28346             
28347             
28348             var store = this.formatCombo.store;
28349             this.formatCombo.setValue("");
28350             for (var i =0; i < ans.length;i++) {
28351                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28352                     // select it..
28353                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28354                     break;
28355                 }
28356             }
28357         }
28358         
28359         
28360         
28361         // hides menus... - so this cant be on a menu...
28362         Roo.bootstrap.MenuMgr.hideAll();
28363         */
28364         Roo.bootstrap.menu.Manager.hideAll();
28365         //this.editorsyncValue();
28366     },
28367     onFirstFocus: function() {
28368         this.buttons.each(function(item){
28369            item.enable();
28370         });
28371     },
28372     toggleSourceEdit : function(sourceEditMode){
28373         
28374           
28375         if(sourceEditMode){
28376             Roo.log("disabling buttons");
28377            this.buttons.each( function(item){
28378                 if(item.cmd != 'pencil'){
28379                     item.disable();
28380                 }
28381             });
28382           
28383         }else{
28384             Roo.log("enabling buttons");
28385             if(this.editorcore.initialized){
28386                 this.buttons.each( function(item){
28387                     item.enable();
28388                 });
28389             }
28390             
28391         }
28392         Roo.log("calling toggole on editor");
28393         // tell the editor that it's been pressed..
28394         this.editor.toggleSourceEdit(sourceEditMode);
28395        
28396     }
28397 });
28398
28399
28400
28401
28402  
28403 /*
28404  * - LGPL
28405  */
28406
28407 /**
28408  * @class Roo.bootstrap.form.Markdown
28409  * @extends Roo.bootstrap.form.TextArea
28410  * Bootstrap Showdown editable area
28411  * @cfg {string} content
28412  * 
28413  * @constructor
28414  * Create a new Showdown
28415  */
28416
28417 Roo.bootstrap.form.Markdown = function(config){
28418     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28419    
28420 };
28421
28422 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
28423     
28424     editing :false,
28425     
28426     initEvents : function()
28427     {
28428         
28429         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28430         this.markdownEl = this.el.createChild({
28431             cls : 'roo-markdown-area'
28432         });
28433         this.inputEl().addClass('d-none');
28434         if (this.getValue() == '') {
28435             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28436             
28437         } else {
28438             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28439         }
28440         this.markdownEl.on('click', this.toggleTextEdit, this);
28441         this.on('blur', this.toggleTextEdit, this);
28442         this.on('specialkey', this.resizeTextArea, this);
28443     },
28444     
28445     toggleTextEdit : function()
28446     {
28447         var sh = this.markdownEl.getHeight();
28448         this.inputEl().addClass('d-none');
28449         this.markdownEl.addClass('d-none');
28450         if (!this.editing) {
28451             // show editor?
28452             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28453             this.inputEl().removeClass('d-none');
28454             this.inputEl().focus();
28455             this.editing = true;
28456             return;
28457         }
28458         // show showdown...
28459         this.updateMarkdown();
28460         this.markdownEl.removeClass('d-none');
28461         this.editing = false;
28462         return;
28463     },
28464     updateMarkdown : function()
28465     {
28466         if (this.getValue() == '') {
28467             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28468             return;
28469         }
28470  
28471         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28472     },
28473     
28474     resizeTextArea: function () {
28475         
28476         var sh = 100;
28477         Roo.log([sh, this.getValue().split("\n").length * 30]);
28478         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28479     },
28480     setValue : function(val)
28481     {
28482         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28483         if (!this.editing) {
28484             this.updateMarkdown();
28485         }
28486         
28487     },
28488     focus : function()
28489     {
28490         if (!this.editing) {
28491             this.toggleTextEdit();
28492         }
28493         
28494     }
28495
28496
28497 });/*
28498  * Based on:
28499  * Ext JS Library 1.1.1
28500  * Copyright(c) 2006-2007, Ext JS, LLC.
28501  *
28502  * Originally Released Under LGPL - original licence link has changed is not relivant.
28503  *
28504  * Fork - LGPL
28505  * <script type="text/javascript">
28506  */
28507  
28508 /**
28509  * @class Roo.bootstrap.PagingToolbar
28510  * @extends Roo.bootstrap.nav.Simplebar
28511  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28512  * @constructor
28513  * Create a new PagingToolbar
28514  * @param {Object} config The config object
28515  * @param {Roo.data.Store} store
28516  */
28517 Roo.bootstrap.PagingToolbar = function(config)
28518 {
28519     // old args format still supported... - xtype is prefered..
28520         // created from xtype...
28521     
28522     this.ds = config.dataSource;
28523     
28524     if (config.store && !this.ds) {
28525         this.store= Roo.factory(config.store, Roo.data);
28526         this.ds = this.store;
28527         this.ds.xmodule = this.xmodule || false;
28528     }
28529     
28530     this.toolbarItems = [];
28531     if (config.items) {
28532         this.toolbarItems = config.items;
28533     }
28534     
28535     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28536     
28537     this.cursor = 0;
28538     
28539     if (this.ds) { 
28540         this.bind(this.ds);
28541     }
28542     
28543     if (Roo.bootstrap.version == 4) {
28544         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28545     } else {
28546         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28547     }
28548     
28549 };
28550
28551 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28552     /**
28553      * @cfg {Roo.bootstrap.Button} buttons[]
28554      * Buttons for the toolbar
28555      */
28556      /**
28557      * @cfg {Roo.data.Store} store
28558      * The underlying data store providing the paged data
28559      */
28560     /**
28561      * @cfg {String/HTMLElement/Element} container
28562      * container The id or element that will contain the toolbar
28563      */
28564     /**
28565      * @cfg {Boolean} displayInfo
28566      * True to display the displayMsg (defaults to false)
28567      */
28568     /**
28569      * @cfg {Number} pageSize
28570      * The number of records to display per page (defaults to 20)
28571      */
28572     pageSize: 20,
28573     /**
28574      * @cfg {String} displayMsg
28575      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28576      */
28577     displayMsg : 'Displaying {0} - {1} of {2}',
28578     /**
28579      * @cfg {String} emptyMsg
28580      * The message to display when no records are found (defaults to "No data to display")
28581      */
28582     emptyMsg : 'No data to display',
28583     /**
28584      * Customizable piece of the default paging text (defaults to "Page")
28585      * @type String
28586      */
28587     beforePageText : "Page",
28588     /**
28589      * Customizable piece of the default paging text (defaults to "of %0")
28590      * @type String
28591      */
28592     afterPageText : "of {0}",
28593     /**
28594      * Customizable piece of the default paging text (defaults to "First Page")
28595      * @type String
28596      */
28597     firstText : "First Page",
28598     /**
28599      * Customizable piece of the default paging text (defaults to "Previous Page")
28600      * @type String
28601      */
28602     prevText : "Previous Page",
28603     /**
28604      * Customizable piece of the default paging text (defaults to "Next Page")
28605      * @type String
28606      */
28607     nextText : "Next Page",
28608     /**
28609      * Customizable piece of the default paging text (defaults to "Last Page")
28610      * @type String
28611      */
28612     lastText : "Last Page",
28613     /**
28614      * Customizable piece of the default paging text (defaults to "Refresh")
28615      * @type String
28616      */
28617     refreshText : "Refresh",
28618
28619     buttons : false,
28620     // private
28621     onRender : function(ct, position) 
28622     {
28623         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28624         this.navgroup.parentId = this.id;
28625         this.navgroup.onRender(this.el, null);
28626         // add the buttons to the navgroup
28627         
28628         if(this.displayInfo){
28629             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28630             this.displayEl = this.el.select('.x-paging-info', true).first();
28631 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28632 //            this.displayEl = navel.el.select('span',true).first();
28633         }
28634         
28635         var _this = this;
28636         
28637         if(this.buttons){
28638             Roo.each(_this.buttons, function(e){ // this might need to use render????
28639                Roo.factory(e).render(_this.el);
28640             });
28641         }
28642             
28643         Roo.each(_this.toolbarItems, function(e) {
28644             _this.navgroup.addItem(e);
28645         });
28646         
28647         
28648         this.first = this.navgroup.addItem({
28649             tooltip: this.firstText,
28650             cls: "prev btn-outline-secondary",
28651             html : ' <i class="fa fa-step-backward"></i>',
28652             disabled: true,
28653             preventDefault: true,
28654             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28655         });
28656         
28657         this.prev =  this.navgroup.addItem({
28658             tooltip: this.prevText,
28659             cls: "prev btn-outline-secondary",
28660             html : ' <i class="fa fa-backward"></i>',
28661             disabled: true,
28662             preventDefault: true,
28663             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28664         });
28665     //this.addSeparator();
28666         
28667         
28668         var field = this.navgroup.addItem( {
28669             tagtype : 'span',
28670             cls : 'x-paging-position  btn-outline-secondary',
28671              disabled: true,
28672             html : this.beforePageText  +
28673                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28674                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28675          } ); //?? escaped?
28676         
28677         this.field = field.el.select('input', true).first();
28678         this.field.on("keydown", this.onPagingKeydown, this);
28679         this.field.on("focus", function(){this.dom.select();});
28680     
28681     
28682         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28683         //this.field.setHeight(18);
28684         //this.addSeparator();
28685         this.next = this.navgroup.addItem({
28686             tooltip: this.nextText,
28687             cls: "next btn-outline-secondary",
28688             html : ' <i class="fa fa-forward"></i>',
28689             disabled: true,
28690             preventDefault: true,
28691             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28692         });
28693         this.last = this.navgroup.addItem({
28694             tooltip: this.lastText,
28695             html : ' <i class="fa fa-step-forward"></i>',
28696             cls: "next btn-outline-secondary",
28697             disabled: true,
28698             preventDefault: true,
28699             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28700         });
28701     //this.addSeparator();
28702         this.loading = this.navgroup.addItem({
28703             tooltip: this.refreshText,
28704             cls: "btn-outline-secondary",
28705             html : ' <i class="fa fa-refresh"></i>',
28706             preventDefault: true,
28707             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28708         });
28709         
28710     },
28711
28712     // private
28713     updateInfo : function(){
28714         if(this.displayEl){
28715             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28716             var msg = count == 0 ?
28717                 this.emptyMsg :
28718                 String.format(
28719                     this.displayMsg,
28720                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28721                 );
28722             this.displayEl.update(msg);
28723         }
28724     },
28725
28726     // private
28727     onLoad : function(ds, r, o)
28728     {
28729         this.cursor = o.params && o.params.start ? o.params.start : 0;
28730         
28731         var d = this.getPageData(),
28732             ap = d.activePage,
28733             ps = d.pages;
28734         
28735         
28736         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28737         this.field.dom.value = ap;
28738         this.first.setDisabled(ap == 1);
28739         this.prev.setDisabled(ap == 1);
28740         this.next.setDisabled(ap == ps);
28741         this.last.setDisabled(ap == ps);
28742         this.loading.enable();
28743         this.updateInfo();
28744     },
28745
28746     // private
28747     getPageData : function(){
28748         var total = this.ds.getTotalCount();
28749         return {
28750             total : total,
28751             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28752             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28753         };
28754     },
28755
28756     // private
28757     onLoadError : function(){
28758         this.loading.enable();
28759     },
28760
28761     // private
28762     onPagingKeydown : function(e){
28763         var k = e.getKey();
28764         var d = this.getPageData();
28765         if(k == e.RETURN){
28766             var v = this.field.dom.value, pageNum;
28767             if(!v || isNaN(pageNum = parseInt(v, 10))){
28768                 this.field.dom.value = d.activePage;
28769                 return;
28770             }
28771             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28772             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28773             e.stopEvent();
28774         }
28775         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))
28776         {
28777           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28778           this.field.dom.value = pageNum;
28779           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28780           e.stopEvent();
28781         }
28782         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28783         {
28784           var v = this.field.dom.value, pageNum; 
28785           var increment = (e.shiftKey) ? 10 : 1;
28786           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28787                 increment *= -1;
28788           }
28789           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28790             this.field.dom.value = d.activePage;
28791             return;
28792           }
28793           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28794           {
28795             this.field.dom.value = parseInt(v, 10) + increment;
28796             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28797             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28798           }
28799           e.stopEvent();
28800         }
28801     },
28802
28803     // private
28804     beforeLoad : function(){
28805         if(this.loading){
28806             this.loading.disable();
28807         }
28808     },
28809
28810     // private
28811     onClick : function(which){
28812         
28813         var ds = this.ds;
28814         if (!ds) {
28815             return;
28816         }
28817         
28818         switch(which){
28819             case "first":
28820                 ds.load({params:{start: 0, limit: this.pageSize}});
28821             break;
28822             case "prev":
28823                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28824             break;
28825             case "next":
28826                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28827             break;
28828             case "last":
28829                 var total = ds.getTotalCount();
28830                 var extra = total % this.pageSize;
28831                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28832                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28833             break;
28834             case "refresh":
28835                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28836             break;
28837         }
28838     },
28839
28840     /**
28841      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28842      * @param {Roo.data.Store} store The data store to unbind
28843      */
28844     unbind : function(ds){
28845         ds.un("beforeload", this.beforeLoad, this);
28846         ds.un("load", this.onLoad, this);
28847         ds.un("loadexception", this.onLoadError, this);
28848         ds.un("remove", this.updateInfo, this);
28849         ds.un("add", this.updateInfo, this);
28850         this.ds = undefined;
28851     },
28852
28853     /**
28854      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28855      * @param {Roo.data.Store} store The data store to bind
28856      */
28857     bind : function(ds){
28858         ds.on("beforeload", this.beforeLoad, this);
28859         ds.on("load", this.onLoad, this);
28860         ds.on("loadexception", this.onLoadError, this);
28861         ds.on("remove", this.updateInfo, this);
28862         ds.on("add", this.updateInfo, this);
28863         this.ds = ds;
28864     }
28865 });/*
28866  * - LGPL
28867  *
28868  * element
28869  * 
28870  */
28871
28872 /**
28873  * @class Roo.bootstrap.MessageBar
28874  * @extends Roo.bootstrap.Component
28875  * Bootstrap MessageBar class
28876  * @cfg {String} html contents of the MessageBar
28877  * @cfg {String} weight (info | success | warning | danger) default info
28878  * @cfg {String} beforeClass insert the bar before the given class
28879  * @cfg {Boolean} closable (true | false) default false
28880  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28881  * 
28882  * @constructor
28883  * Create a new Element
28884  * @param {Object} config The config object
28885  */
28886
28887 Roo.bootstrap.MessageBar = function(config){
28888     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28889 };
28890
28891 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28892     
28893     html: '',
28894     weight: 'info',
28895     closable: false,
28896     fixed: false,
28897     beforeClass: 'bootstrap-sticky-wrap',
28898     
28899     getAutoCreate : function(){
28900         
28901         var cfg = {
28902             tag: 'div',
28903             cls: 'alert alert-dismissable alert-' + this.weight,
28904             cn: [
28905                 {
28906                     tag: 'span',
28907                     cls: 'message',
28908                     html: this.html || ''
28909                 }
28910             ]
28911         };
28912         
28913         if(this.fixed){
28914             cfg.cls += ' alert-messages-fixed';
28915         }
28916         
28917         if(this.closable){
28918             cfg.cn.push({
28919                 tag: 'button',
28920                 cls: 'close',
28921                 html: 'x'
28922             });
28923         }
28924         
28925         return cfg;
28926     },
28927     
28928     onRender : function(ct, position)
28929     {
28930         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28931         
28932         if(!this.el){
28933             var cfg = Roo.apply({},  this.getAutoCreate());
28934             cfg.id = Roo.id();
28935             
28936             if (this.cls) {
28937                 cfg.cls += ' ' + this.cls;
28938             }
28939             if (this.style) {
28940                 cfg.style = this.style;
28941             }
28942             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28943             
28944             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28945         }
28946         
28947         this.el.select('>button.close').on('click', this.hide, this);
28948         
28949     },
28950     
28951     show : function()
28952     {
28953         if (!this.rendered) {
28954             this.render();
28955         }
28956         
28957         this.el.show();
28958         
28959         this.fireEvent('show', this);
28960         
28961     },
28962     
28963     hide : function()
28964     {
28965         if (!this.rendered) {
28966             this.render();
28967         }
28968         
28969         this.el.hide();
28970         
28971         this.fireEvent('hide', this);
28972     },
28973     
28974     update : function()
28975     {
28976 //        var e = this.el.dom.firstChild;
28977 //        
28978 //        if(this.closable){
28979 //            e = e.nextSibling;
28980 //        }
28981 //        
28982 //        e.data = this.html || '';
28983
28984         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28985     }
28986    
28987 });
28988
28989  
28990
28991      /*
28992  * - LGPL
28993  *
28994  * Graph
28995  * 
28996  */
28997
28998
28999 /**
29000  * @class Roo.bootstrap.Graph
29001  * @extends Roo.bootstrap.Component
29002  * Bootstrap Graph class
29003 > Prameters
29004  -sm {number} sm 4
29005  -md {number} md 5
29006  @cfg {String} graphtype  bar | vbar | pie
29007  @cfg {number} g_x coodinator | centre x (pie)
29008  @cfg {number} g_y coodinator | centre y (pie)
29009  @cfg {number} g_r radius (pie)
29010  @cfg {number} g_height height of the chart (respected by all elements in the set)
29011  @cfg {number} g_width width of the chart (respected by all elements in the set)
29012  @cfg {Object} title The title of the chart
29013     
29014  -{Array}  values
29015  -opts (object) options for the chart 
29016      o {
29017      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29018      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29019      o vgutter (number)
29020      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.
29021      o stacked (boolean) whether or not to tread values as in a stacked bar chart
29022      o to
29023      o stretch (boolean)
29024      o }
29025  -opts (object) options for the pie
29026      o{
29027      o cut
29028      o startAngle (number)
29029      o endAngle (number)
29030      } 
29031  *
29032  * @constructor
29033  * Create a new Input
29034  * @param {Object} config The config object
29035  */
29036
29037 Roo.bootstrap.Graph = function(config){
29038     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29039     
29040     this.addEvents({
29041         // img events
29042         /**
29043          * @event click
29044          * The img click event for the img.
29045          * @param {Roo.EventObject} e
29046          */
29047         "click" : true
29048     });
29049 };
29050
29051 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
29052     
29053     sm: 4,
29054     md: 5,
29055     graphtype: 'bar',
29056     g_height: 250,
29057     g_width: 400,
29058     g_x: 50,
29059     g_y: 50,
29060     g_r: 30,
29061     opts:{
29062         //g_colors: this.colors,
29063         g_type: 'soft',
29064         g_gutter: '20%'
29065
29066     },
29067     title : false,
29068
29069     getAutoCreate : function(){
29070         
29071         var cfg = {
29072             tag: 'div',
29073             html : null
29074         };
29075         
29076         
29077         return  cfg;
29078     },
29079
29080     onRender : function(ct,position){
29081         
29082         
29083         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29084         
29085         if (typeof(Raphael) == 'undefined') {
29086             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29087             return;
29088         }
29089         
29090         this.raphael = Raphael(this.el.dom);
29091         
29092                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29093                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29094                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29095                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29096                 /*
29097                 r.text(160, 10, "Single Series Chart").attr(txtattr);
29098                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29099                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29100                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29101                 
29102                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29103                 r.barchart(330, 10, 300, 220, data1);
29104                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29105                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29106                 */
29107                 
29108                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29109                 // r.barchart(30, 30, 560, 250,  xdata, {
29110                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29111                 //     axis : "0 0 1 1",
29112                 //     axisxlabels :  xdata
29113                 //     //yvalues : cols,
29114                    
29115                 // });
29116 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29117 //        
29118 //        this.load(null,xdata,{
29119 //                axis : "0 0 1 1",
29120 //                axisxlabels :  xdata
29121 //                });
29122
29123     },
29124
29125     load : function(graphtype,xdata,opts)
29126     {
29127         this.raphael.clear();
29128         if(!graphtype) {
29129             graphtype = this.graphtype;
29130         }
29131         if(!opts){
29132             opts = this.opts;
29133         }
29134         var r = this.raphael,
29135             fin = function () {
29136                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29137             },
29138             fout = function () {
29139                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29140             },
29141             pfin = function() {
29142                 this.sector.stop();
29143                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29144
29145                 if (this.label) {
29146                     this.label[0].stop();
29147                     this.label[0].attr({ r: 7.5 });
29148                     this.label[1].attr({ "font-weight": 800 });
29149                 }
29150             },
29151             pfout = function() {
29152                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29153
29154                 if (this.label) {
29155                     this.label[0].animate({ r: 5 }, 500, "bounce");
29156                     this.label[1].attr({ "font-weight": 400 });
29157                 }
29158             };
29159
29160         switch(graphtype){
29161             case 'bar':
29162                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29163                 break;
29164             case 'hbar':
29165                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29166                 break;
29167             case 'pie':
29168 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
29169 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29170 //            
29171                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29172                 
29173                 break;
29174
29175         }
29176         
29177         if(this.title){
29178             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29179         }
29180         
29181     },
29182     
29183     setTitle: function(o)
29184     {
29185         this.title = o;
29186     },
29187     
29188     initEvents: function() {
29189         
29190         if(!this.href){
29191             this.el.on('click', this.onClick, this);
29192         }
29193     },
29194     
29195     onClick : function(e)
29196     {
29197         Roo.log('img onclick');
29198         this.fireEvent('click', this, e);
29199     }
29200    
29201 });
29202
29203  
29204 /*
29205  * - LGPL
29206  *
29207  * numberBox
29208  * 
29209  */
29210 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29211
29212 /**
29213  * @class Roo.bootstrap.dash.NumberBox
29214  * @extends Roo.bootstrap.Component
29215  * Bootstrap NumberBox class
29216  * @cfg {String} headline Box headline
29217  * @cfg {String} content Box content
29218  * @cfg {String} icon Box icon
29219  * @cfg {String} footer Footer text
29220  * @cfg {String} fhref Footer href
29221  * 
29222  * @constructor
29223  * Create a new NumberBox
29224  * @param {Object} config The config object
29225  */
29226
29227
29228 Roo.bootstrap.dash.NumberBox = function(config){
29229     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29230     
29231 };
29232
29233 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
29234     
29235     headline : '',
29236     content : '',
29237     icon : '',
29238     footer : '',
29239     fhref : '',
29240     ficon : '',
29241     
29242     getAutoCreate : function(){
29243         
29244         var cfg = {
29245             tag : 'div',
29246             cls : 'small-box ',
29247             cn : [
29248                 {
29249                     tag : 'div',
29250                     cls : 'inner',
29251                     cn :[
29252                         {
29253                             tag : 'h3',
29254                             cls : 'roo-headline',
29255                             html : this.headline
29256                         },
29257                         {
29258                             tag : 'p',
29259                             cls : 'roo-content',
29260                             html : this.content
29261                         }
29262                     ]
29263                 }
29264             ]
29265         };
29266         
29267         if(this.icon){
29268             cfg.cn.push({
29269                 tag : 'div',
29270                 cls : 'icon',
29271                 cn :[
29272                     {
29273                         tag : 'i',
29274                         cls : 'ion ' + this.icon
29275                     }
29276                 ]
29277             });
29278         }
29279         
29280         if(this.footer){
29281             var footer = {
29282                 tag : 'a',
29283                 cls : 'small-box-footer',
29284                 href : this.fhref || '#',
29285                 html : this.footer
29286             };
29287             
29288             cfg.cn.push(footer);
29289             
29290         }
29291         
29292         return  cfg;
29293     },
29294
29295     onRender : function(ct,position){
29296         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29297
29298
29299        
29300                 
29301     },
29302
29303     setHeadline: function (value)
29304     {
29305         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29306     },
29307     
29308     setFooter: function (value, href)
29309     {
29310         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29311         
29312         if(href){
29313             this.el.select('a.small-box-footer',true).first().attr('href', href);
29314         }
29315         
29316     },
29317
29318     setContent: function (value)
29319     {
29320         this.el.select('.roo-content',true).first().dom.innerHTML = value;
29321     },
29322
29323     initEvents: function() 
29324     {   
29325         
29326     }
29327     
29328 });
29329
29330  
29331 /*
29332  * - LGPL
29333  *
29334  * TabBox
29335  * 
29336  */
29337 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29338
29339 /**
29340  * @class Roo.bootstrap.dash.TabBox
29341  * @extends Roo.bootstrap.Component
29342  * @children Roo.bootstrap.dash.TabPane
29343  * Bootstrap TabBox class
29344  * @cfg {String} title Title of the TabBox
29345  * @cfg {String} icon Icon of the TabBox
29346  * @cfg {Boolean} showtabs (true|false) show the tabs default true
29347  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29348  * 
29349  * @constructor
29350  * Create a new TabBox
29351  * @param {Object} config The config object
29352  */
29353
29354
29355 Roo.bootstrap.dash.TabBox = function(config){
29356     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29357     this.addEvents({
29358         // raw events
29359         /**
29360          * @event addpane
29361          * When a pane is added
29362          * @param {Roo.bootstrap.dash.TabPane} pane
29363          */
29364         "addpane" : true,
29365         /**
29366          * @event activatepane
29367          * When a pane is activated
29368          * @param {Roo.bootstrap.dash.TabPane} pane
29369          */
29370         "activatepane" : true
29371         
29372          
29373     });
29374     
29375     this.panes = [];
29376 };
29377
29378 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
29379
29380     title : '',
29381     icon : false,
29382     showtabs : true,
29383     tabScrollable : false,
29384     
29385     getChildContainer : function()
29386     {
29387         return this.el.select('.tab-content', true).first();
29388     },
29389     
29390     getAutoCreate : function(){
29391         
29392         var header = {
29393             tag: 'li',
29394             cls: 'pull-left header',
29395             html: this.title,
29396             cn : []
29397         };
29398         
29399         if(this.icon){
29400             header.cn.push({
29401                 tag: 'i',
29402                 cls: 'fa ' + this.icon
29403             });
29404         }
29405         
29406         var h = {
29407             tag: 'ul',
29408             cls: 'nav nav-tabs pull-right',
29409             cn: [
29410                 header
29411             ]
29412         };
29413         
29414         if(this.tabScrollable){
29415             h = {
29416                 tag: 'div',
29417                 cls: 'tab-header',
29418                 cn: [
29419                     {
29420                         tag: 'ul',
29421                         cls: 'nav nav-tabs pull-right',
29422                         cn: [
29423                             header
29424                         ]
29425                     }
29426                 ]
29427             };
29428         }
29429         
29430         var cfg = {
29431             tag: 'div',
29432             cls: 'nav-tabs-custom',
29433             cn: [
29434                 h,
29435                 {
29436                     tag: 'div',
29437                     cls: 'tab-content no-padding',
29438                     cn: []
29439                 }
29440             ]
29441         };
29442
29443         return  cfg;
29444     },
29445     initEvents : function()
29446     {
29447         //Roo.log('add add pane handler');
29448         this.on('addpane', this.onAddPane, this);
29449     },
29450      /**
29451      * Updates the box title
29452      * @param {String} html to set the title to.
29453      */
29454     setTitle : function(value)
29455     {
29456         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29457     },
29458     onAddPane : function(pane)
29459     {
29460         this.panes.push(pane);
29461         //Roo.log('addpane');
29462         //Roo.log(pane);
29463         // tabs are rendere left to right..
29464         if(!this.showtabs){
29465             return;
29466         }
29467         
29468         var ctr = this.el.select('.nav-tabs', true).first();
29469          
29470          
29471         var existing = ctr.select('.nav-tab',true);
29472         var qty = existing.getCount();;
29473         
29474         
29475         var tab = ctr.createChild({
29476             tag : 'li',
29477             cls : 'nav-tab' + (qty ? '' : ' active'),
29478             cn : [
29479                 {
29480                     tag : 'a',
29481                     href:'#',
29482                     html : pane.title
29483                 }
29484             ]
29485         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29486         pane.tab = tab;
29487         
29488         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29489         if (!qty) {
29490             pane.el.addClass('active');
29491         }
29492         
29493                 
29494     },
29495     onTabClick : function(ev,un,ob,pane)
29496     {
29497         //Roo.log('tab - prev default');
29498         ev.preventDefault();
29499         
29500         
29501         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29502         pane.tab.addClass('active');
29503         //Roo.log(pane.title);
29504         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29505         // technically we should have a deactivate event.. but maybe add later.
29506         // and it should not de-activate the selected tab...
29507         this.fireEvent('activatepane', pane);
29508         pane.el.addClass('active');
29509         pane.fireEvent('activate');
29510         
29511         
29512     },
29513     
29514     getActivePane : function()
29515     {
29516         var r = false;
29517         Roo.each(this.panes, function(p) {
29518             if(p.el.hasClass('active')){
29519                 r = p;
29520                 return false;
29521             }
29522             
29523             return;
29524         });
29525         
29526         return r;
29527     }
29528     
29529     
29530 });
29531
29532  
29533 /*
29534  * - LGPL
29535  *
29536  * Tab pane
29537  * 
29538  */
29539 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29540 /**
29541  * @class Roo.bootstrap.TabPane
29542  * @extends Roo.bootstrap.Component
29543  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
29544  * Bootstrap TabPane class
29545  * @cfg {Boolean} active (false | true) Default false
29546  * @cfg {String} title title of panel
29547
29548  * 
29549  * @constructor
29550  * Create a new TabPane
29551  * @param {Object} config The config object
29552  */
29553
29554 Roo.bootstrap.dash.TabPane = function(config){
29555     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29556     
29557     this.addEvents({
29558         // raw events
29559         /**
29560          * @event activate
29561          * When a pane is activated
29562          * @param {Roo.bootstrap.dash.TabPane} pane
29563          */
29564         "activate" : true
29565          
29566     });
29567 };
29568
29569 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29570     
29571     active : false,
29572     title : '',
29573     
29574     // the tabBox that this is attached to.
29575     tab : false,
29576      
29577     getAutoCreate : function() 
29578     {
29579         var cfg = {
29580             tag: 'div',
29581             cls: 'tab-pane'
29582         };
29583         
29584         if(this.active){
29585             cfg.cls += ' active';
29586         }
29587         
29588         return cfg;
29589     },
29590     initEvents  : function()
29591     {
29592         //Roo.log('trigger add pane handler');
29593         this.parent().fireEvent('addpane', this)
29594     },
29595     
29596      /**
29597      * Updates the tab title 
29598      * @param {String} html to set the title to.
29599      */
29600     setTitle: function(str)
29601     {
29602         if (!this.tab) {
29603             return;
29604         }
29605         this.title = str;
29606         this.tab.select('a', true).first().dom.innerHTML = str;
29607         
29608     }
29609     
29610     
29611     
29612 });
29613
29614  
29615
29616
29617  /*
29618  * - LGPL
29619  *
29620  * Tooltip
29621  * 
29622  */
29623
29624 /**
29625  * @class Roo.bootstrap.Tooltip
29626  * Bootstrap Tooltip class
29627  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29628  * to determine which dom element triggers the tooltip.
29629  * 
29630  * It needs to add support for additional attributes like tooltip-position
29631  * 
29632  * @constructor
29633  * Create a new Toolti
29634  * @param {Object} config The config object
29635  */
29636
29637 Roo.bootstrap.Tooltip = function(config){
29638     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29639     
29640     this.alignment = Roo.bootstrap.Tooltip.alignment;
29641     
29642     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29643         this.alignment = config.alignment;
29644     }
29645     
29646 };
29647
29648 Roo.apply(Roo.bootstrap.Tooltip, {
29649     /**
29650      * @function init initialize tooltip monitoring.
29651      * @static
29652      */
29653     currentEl : false,
29654     currentTip : false,
29655     currentRegion : false,
29656     
29657     //  init : delay?
29658     
29659     init : function()
29660     {
29661         Roo.get(document).on('mouseover', this.enter ,this);
29662         Roo.get(document).on('mouseout', this.leave, this);
29663          
29664         
29665         this.currentTip = new Roo.bootstrap.Tooltip();
29666     },
29667     
29668     enter : function(ev)
29669     {
29670         var dom = ev.getTarget();
29671         
29672         //Roo.log(['enter',dom]);
29673         var el = Roo.fly(dom);
29674         if (this.currentEl) {
29675             //Roo.log(dom);
29676             //Roo.log(this.currentEl);
29677             //Roo.log(this.currentEl.contains(dom));
29678             if (this.currentEl == el) {
29679                 return;
29680             }
29681             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29682                 return;
29683             }
29684
29685         }
29686         
29687         if (this.currentTip.el) {
29688             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29689         }    
29690         //Roo.log(ev);
29691         
29692         if(!el || el.dom == document){
29693             return;
29694         }
29695         
29696         var bindEl = el; 
29697         var pel = false;
29698         if (!el.attr('tooltip')) {
29699             pel = el.findParent("[tooltip]");
29700             if (pel) {
29701                 bindEl = Roo.get(pel);
29702             }
29703         }
29704         
29705        
29706         
29707         // you can not look for children, as if el is the body.. then everythign is the child..
29708         if (!pel && !el.attr('tooltip')) { //
29709             if (!el.select("[tooltip]").elements.length) {
29710                 return;
29711             }
29712             // is the mouse over this child...?
29713             bindEl = el.select("[tooltip]").first();
29714             var xy = ev.getXY();
29715             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29716                 //Roo.log("not in region.");
29717                 return;
29718             }
29719             //Roo.log("child element over..");
29720             
29721         }
29722         this.currentEl = el;
29723         this.currentTip.bind(bindEl);
29724         this.currentRegion = Roo.lib.Region.getRegion(dom);
29725         this.currentTip.enter();
29726         
29727     },
29728     leave : function(ev)
29729     {
29730         var dom = ev.getTarget();
29731         //Roo.log(['leave',dom]);
29732         if (!this.currentEl) {
29733             return;
29734         }
29735         
29736         
29737         if (dom != this.currentEl.dom) {
29738             return;
29739         }
29740         var xy = ev.getXY();
29741         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29742             return;
29743         }
29744         // only activate leave if mouse cursor is outside... bounding box..
29745         
29746         
29747         
29748         
29749         if (this.currentTip) {
29750             this.currentTip.leave();
29751         }
29752         //Roo.log('clear currentEl');
29753         this.currentEl = false;
29754         
29755         
29756     },
29757     alignment : {
29758         'left' : ['r-l', [-2,0], 'right'],
29759         'right' : ['l-r', [2,0], 'left'],
29760         'bottom' : ['t-b', [0,2], 'top'],
29761         'top' : [ 'b-t', [0,-2], 'bottom']
29762     }
29763     
29764 });
29765
29766
29767 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29768     
29769     
29770     bindEl : false,
29771     
29772     delay : null, // can be { show : 300 , hide: 500}
29773     
29774     timeout : null,
29775     
29776     hoverState : null, //???
29777     
29778     placement : 'bottom', 
29779     
29780     alignment : false,
29781     
29782     getAutoCreate : function(){
29783     
29784         var cfg = {
29785            cls : 'tooltip',   
29786            role : 'tooltip',
29787            cn : [
29788                 {
29789                     cls : 'tooltip-arrow arrow'
29790                 },
29791                 {
29792                     cls : 'tooltip-inner'
29793                 }
29794            ]
29795         };
29796         
29797         return cfg;
29798     },
29799     bind : function(el)
29800     {
29801         this.bindEl = el;
29802     },
29803     
29804     initEvents : function()
29805     {
29806         this.arrowEl = this.el.select('.arrow', true).first();
29807         this.innerEl = this.el.select('.tooltip-inner', true).first();
29808     },
29809     
29810     enter : function () {
29811        
29812         if (this.timeout != null) {
29813             clearTimeout(this.timeout);
29814         }
29815         
29816         this.hoverState = 'in';
29817          //Roo.log("enter - show");
29818         if (!this.delay || !this.delay.show) {
29819             this.show();
29820             return;
29821         }
29822         var _t = this;
29823         this.timeout = setTimeout(function () {
29824             if (_t.hoverState == 'in') {
29825                 _t.show();
29826             }
29827         }, this.delay.show);
29828     },
29829     leave : function()
29830     {
29831         clearTimeout(this.timeout);
29832     
29833         this.hoverState = 'out';
29834          if (!this.delay || !this.delay.hide) {
29835             this.hide();
29836             return;
29837         }
29838        
29839         var _t = this;
29840         this.timeout = setTimeout(function () {
29841             //Roo.log("leave - timeout");
29842             
29843             if (_t.hoverState == 'out') {
29844                 _t.hide();
29845                 Roo.bootstrap.Tooltip.currentEl = false;
29846             }
29847         }, delay);
29848     },
29849     
29850     show : function (msg)
29851     {
29852         if (!this.el) {
29853             this.render(document.body);
29854         }
29855         // set content.
29856         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29857         
29858         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29859         
29860         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29861         
29862         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29863                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29864         
29865         var placement = typeof this.placement == 'function' ?
29866             this.placement.call(this, this.el, on_el) :
29867             this.placement;
29868             
29869         var autoToken = /\s?auto?\s?/i;
29870         var autoPlace = autoToken.test(placement);
29871         if (autoPlace) {
29872             placement = placement.replace(autoToken, '') || 'top';
29873         }
29874         
29875         //this.el.detach()
29876         //this.el.setXY([0,0]);
29877         this.el.show();
29878         //this.el.dom.style.display='block';
29879         
29880         //this.el.appendTo(on_el);
29881         
29882         var p = this.getPosition();
29883         var box = this.el.getBox();
29884         
29885         if (autoPlace) {
29886             // fixme..
29887         }
29888         
29889         var align = this.alignment[placement];
29890         
29891         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29892         
29893         if(placement == 'top' || placement == 'bottom'){
29894             if(xy[0] < 0){
29895                 placement = 'right';
29896             }
29897             
29898             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29899                 placement = 'left';
29900             }
29901             
29902             var scroll = Roo.select('body', true).first().getScroll();
29903             
29904             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29905                 placement = 'top';
29906             }
29907             
29908             align = this.alignment[placement];
29909             
29910             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29911             
29912         }
29913         
29914         var elems = document.getElementsByTagName('div');
29915         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29916         for (var i = 0; i < elems.length; i++) {
29917           var zindex = Number.parseInt(
29918                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29919                 10
29920           );
29921           if (zindex > highest) {
29922             highest = zindex;
29923           }
29924         }
29925         
29926         
29927         
29928         this.el.dom.style.zIndex = highest;
29929         
29930         this.el.alignTo(this.bindEl, align[0],align[1]);
29931         //var arrow = this.el.select('.arrow',true).first();
29932         //arrow.set(align[2], 
29933         
29934         this.el.addClass(placement);
29935         this.el.addClass("bs-tooltip-"+ placement);
29936         
29937         this.el.addClass('in fade show');
29938         
29939         this.hoverState = null;
29940         
29941         if (this.el.hasClass('fade')) {
29942             // fade it?
29943         }
29944         
29945         
29946         
29947         
29948         
29949     },
29950     hide : function()
29951     {
29952          
29953         if (!this.el) {
29954             return;
29955         }
29956         //this.el.setXY([0,0]);
29957         this.el.removeClass(['show', 'in']);
29958         //this.el.hide();
29959         
29960     }
29961     
29962 });
29963  
29964
29965  /*
29966  * - LGPL
29967  *
29968  * Location Picker
29969  * 
29970  */
29971
29972 /**
29973  * @class Roo.bootstrap.LocationPicker
29974  * @extends Roo.bootstrap.Component
29975  * Bootstrap LocationPicker class
29976  * @cfg {Number} latitude Position when init default 0
29977  * @cfg {Number} longitude Position when init default 0
29978  * @cfg {Number} zoom default 15
29979  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29980  * @cfg {Boolean} mapTypeControl default false
29981  * @cfg {Boolean} disableDoubleClickZoom default false
29982  * @cfg {Boolean} scrollwheel default true
29983  * @cfg {Boolean} streetViewControl default false
29984  * @cfg {Number} radius default 0
29985  * @cfg {String} locationName
29986  * @cfg {Boolean} draggable default true
29987  * @cfg {Boolean} enableAutocomplete default false
29988  * @cfg {Boolean} enableReverseGeocode default true
29989  * @cfg {String} markerTitle
29990  * 
29991  * @constructor
29992  * Create a new LocationPicker
29993  * @param {Object} config The config object
29994  */
29995
29996
29997 Roo.bootstrap.LocationPicker = function(config){
29998     
29999     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30000     
30001     this.addEvents({
30002         /**
30003          * @event initial
30004          * Fires when the picker initialized.
30005          * @param {Roo.bootstrap.LocationPicker} this
30006          * @param {Google Location} location
30007          */
30008         initial : true,
30009         /**
30010          * @event positionchanged
30011          * Fires when the picker position changed.
30012          * @param {Roo.bootstrap.LocationPicker} this
30013          * @param {Google Location} location
30014          */
30015         positionchanged : true,
30016         /**
30017          * @event resize
30018          * Fires when the map resize.
30019          * @param {Roo.bootstrap.LocationPicker} this
30020          */
30021         resize : true,
30022         /**
30023          * @event show
30024          * Fires when the map show.
30025          * @param {Roo.bootstrap.LocationPicker} this
30026          */
30027         show : true,
30028         /**
30029          * @event hide
30030          * Fires when the map hide.
30031          * @param {Roo.bootstrap.LocationPicker} this
30032          */
30033         hide : true,
30034         /**
30035          * @event mapClick
30036          * Fires when click the map.
30037          * @param {Roo.bootstrap.LocationPicker} this
30038          * @param {Map event} e
30039          */
30040         mapClick : true,
30041         /**
30042          * @event mapRightClick
30043          * Fires when right click the map.
30044          * @param {Roo.bootstrap.LocationPicker} this
30045          * @param {Map event} e
30046          */
30047         mapRightClick : true,
30048         /**
30049          * @event markerClick
30050          * Fires when click the marker.
30051          * @param {Roo.bootstrap.LocationPicker} this
30052          * @param {Map event} e
30053          */
30054         markerClick : true,
30055         /**
30056          * @event markerRightClick
30057          * Fires when right click the marker.
30058          * @param {Roo.bootstrap.LocationPicker} this
30059          * @param {Map event} e
30060          */
30061         markerRightClick : true,
30062         /**
30063          * @event OverlayViewDraw
30064          * Fires when OverlayView Draw
30065          * @param {Roo.bootstrap.LocationPicker} this
30066          */
30067         OverlayViewDraw : true,
30068         /**
30069          * @event OverlayViewOnAdd
30070          * Fires when OverlayView Draw
30071          * @param {Roo.bootstrap.LocationPicker} this
30072          */
30073         OverlayViewOnAdd : true,
30074         /**
30075          * @event OverlayViewOnRemove
30076          * Fires when OverlayView Draw
30077          * @param {Roo.bootstrap.LocationPicker} this
30078          */
30079         OverlayViewOnRemove : true,
30080         /**
30081          * @event OverlayViewShow
30082          * Fires when OverlayView Draw
30083          * @param {Roo.bootstrap.LocationPicker} this
30084          * @param {Pixel} cpx
30085          */
30086         OverlayViewShow : true,
30087         /**
30088          * @event OverlayViewHide
30089          * Fires when OverlayView Draw
30090          * @param {Roo.bootstrap.LocationPicker} this
30091          */
30092         OverlayViewHide : true,
30093         /**
30094          * @event loadexception
30095          * Fires when load google lib failed.
30096          * @param {Roo.bootstrap.LocationPicker} this
30097          */
30098         loadexception : true
30099     });
30100         
30101 };
30102
30103 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30104     
30105     gMapContext: false,
30106     
30107     latitude: 0,
30108     longitude: 0,
30109     zoom: 15,
30110     mapTypeId: false,
30111     mapTypeControl: false,
30112     disableDoubleClickZoom: false,
30113     scrollwheel: true,
30114     streetViewControl: false,
30115     radius: 0,
30116     locationName: '',
30117     draggable: true,
30118     enableAutocomplete: false,
30119     enableReverseGeocode: true,
30120     markerTitle: '',
30121     
30122     getAutoCreate: function()
30123     {
30124
30125         var cfg = {
30126             tag: 'div',
30127             cls: 'roo-location-picker'
30128         };
30129         
30130         return cfg
30131     },
30132     
30133     initEvents: function(ct, position)
30134     {       
30135         if(!this.el.getWidth() || this.isApplied()){
30136             return;
30137         }
30138         
30139         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30140         
30141         this.initial();
30142     },
30143     
30144     initial: function()
30145     {
30146         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30147             this.fireEvent('loadexception', this);
30148             return;
30149         }
30150         
30151         if(!this.mapTypeId){
30152             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30153         }
30154         
30155         this.gMapContext = this.GMapContext();
30156         
30157         this.initOverlayView();
30158         
30159         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30160         
30161         var _this = this;
30162                 
30163         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30164             _this.setPosition(_this.gMapContext.marker.position);
30165         });
30166         
30167         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30168             _this.fireEvent('mapClick', this, event);
30169             
30170         });
30171
30172         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30173             _this.fireEvent('mapRightClick', this, event);
30174             
30175         });
30176         
30177         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30178             _this.fireEvent('markerClick', this, event);
30179             
30180         });
30181
30182         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30183             _this.fireEvent('markerRightClick', this, event);
30184             
30185         });
30186         
30187         this.setPosition(this.gMapContext.location);
30188         
30189         this.fireEvent('initial', this, this.gMapContext.location);
30190     },
30191     
30192     initOverlayView: function()
30193     {
30194         var _this = this;
30195         
30196         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30197             
30198             draw: function()
30199             {
30200                 _this.fireEvent('OverlayViewDraw', _this);
30201             },
30202             
30203             onAdd: function()
30204             {
30205                 _this.fireEvent('OverlayViewOnAdd', _this);
30206             },
30207             
30208             onRemove: function()
30209             {
30210                 _this.fireEvent('OverlayViewOnRemove', _this);
30211             },
30212             
30213             show: function(cpx)
30214             {
30215                 _this.fireEvent('OverlayViewShow', _this, cpx);
30216             },
30217             
30218             hide: function()
30219             {
30220                 _this.fireEvent('OverlayViewHide', _this);
30221             }
30222             
30223         });
30224     },
30225     
30226     fromLatLngToContainerPixel: function(event)
30227     {
30228         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30229     },
30230     
30231     isApplied: function() 
30232     {
30233         return this.getGmapContext() == false ? false : true;
30234     },
30235     
30236     getGmapContext: function() 
30237     {
30238         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30239     },
30240     
30241     GMapContext: function() 
30242     {
30243         var position = new google.maps.LatLng(this.latitude, this.longitude);
30244         
30245         var _map = new google.maps.Map(this.el.dom, {
30246             center: position,
30247             zoom: this.zoom,
30248             mapTypeId: this.mapTypeId,
30249             mapTypeControl: this.mapTypeControl,
30250             disableDoubleClickZoom: this.disableDoubleClickZoom,
30251             scrollwheel: this.scrollwheel,
30252             streetViewControl: this.streetViewControl,
30253             locationName: this.locationName,
30254             draggable: this.draggable,
30255             enableAutocomplete: this.enableAutocomplete,
30256             enableReverseGeocode: this.enableReverseGeocode
30257         });
30258         
30259         var _marker = new google.maps.Marker({
30260             position: position,
30261             map: _map,
30262             title: this.markerTitle,
30263             draggable: this.draggable
30264         });
30265         
30266         return {
30267             map: _map,
30268             marker: _marker,
30269             circle: null,
30270             location: position,
30271             radius: this.radius,
30272             locationName: this.locationName,
30273             addressComponents: {
30274                 formatted_address: null,
30275                 addressLine1: null,
30276                 addressLine2: null,
30277                 streetName: null,
30278                 streetNumber: null,
30279                 city: null,
30280                 district: null,
30281                 state: null,
30282                 stateOrProvince: null
30283             },
30284             settings: this,
30285             domContainer: this.el.dom,
30286             geodecoder: new google.maps.Geocoder()
30287         };
30288     },
30289     
30290     drawCircle: function(center, radius, options) 
30291     {
30292         if (this.gMapContext.circle != null) {
30293             this.gMapContext.circle.setMap(null);
30294         }
30295         if (radius > 0) {
30296             radius *= 1;
30297             options = Roo.apply({}, options, {
30298                 strokeColor: "#0000FF",
30299                 strokeOpacity: .35,
30300                 strokeWeight: 2,
30301                 fillColor: "#0000FF",
30302                 fillOpacity: .2
30303             });
30304             
30305             options.map = this.gMapContext.map;
30306             options.radius = radius;
30307             options.center = center;
30308             this.gMapContext.circle = new google.maps.Circle(options);
30309             return this.gMapContext.circle;
30310         }
30311         
30312         return null;
30313     },
30314     
30315     setPosition: function(location) 
30316     {
30317         this.gMapContext.location = location;
30318         this.gMapContext.marker.setPosition(location);
30319         this.gMapContext.map.panTo(location);
30320         this.drawCircle(location, this.gMapContext.radius, {});
30321         
30322         var _this = this;
30323         
30324         if (this.gMapContext.settings.enableReverseGeocode) {
30325             this.gMapContext.geodecoder.geocode({
30326                 latLng: this.gMapContext.location
30327             }, function(results, status) {
30328                 
30329                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30330                     _this.gMapContext.locationName = results[0].formatted_address;
30331                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30332                     
30333                     _this.fireEvent('positionchanged', this, location);
30334                 }
30335             });
30336             
30337             return;
30338         }
30339         
30340         this.fireEvent('positionchanged', this, location);
30341     },
30342     
30343     resize: function()
30344     {
30345         google.maps.event.trigger(this.gMapContext.map, "resize");
30346         
30347         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30348         
30349         this.fireEvent('resize', this);
30350     },
30351     
30352     setPositionByLatLng: function(latitude, longitude)
30353     {
30354         this.setPosition(new google.maps.LatLng(latitude, longitude));
30355     },
30356     
30357     getCurrentPosition: function() 
30358     {
30359         return {
30360             latitude: this.gMapContext.location.lat(),
30361             longitude: this.gMapContext.location.lng()
30362         };
30363     },
30364     
30365     getAddressName: function() 
30366     {
30367         return this.gMapContext.locationName;
30368     },
30369     
30370     getAddressComponents: function() 
30371     {
30372         return this.gMapContext.addressComponents;
30373     },
30374     
30375     address_component_from_google_geocode: function(address_components) 
30376     {
30377         var result = {};
30378         
30379         for (var i = 0; i < address_components.length; i++) {
30380             var component = address_components[i];
30381             if (component.types.indexOf("postal_code") >= 0) {
30382                 result.postalCode = component.short_name;
30383             } else if (component.types.indexOf("street_number") >= 0) {
30384                 result.streetNumber = component.short_name;
30385             } else if (component.types.indexOf("route") >= 0) {
30386                 result.streetName = component.short_name;
30387             } else if (component.types.indexOf("neighborhood") >= 0) {
30388                 result.city = component.short_name;
30389             } else if (component.types.indexOf("locality") >= 0) {
30390                 result.city = component.short_name;
30391             } else if (component.types.indexOf("sublocality") >= 0) {
30392                 result.district = component.short_name;
30393             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30394                 result.stateOrProvince = component.short_name;
30395             } else if (component.types.indexOf("country") >= 0) {
30396                 result.country = component.short_name;
30397             }
30398         }
30399         
30400         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30401         result.addressLine2 = "";
30402         return result;
30403     },
30404     
30405     setZoomLevel: function(zoom)
30406     {
30407         this.gMapContext.map.setZoom(zoom);
30408     },
30409     
30410     show: function()
30411     {
30412         if(!this.el){
30413             return;
30414         }
30415         
30416         this.el.show();
30417         
30418         this.resize();
30419         
30420         this.fireEvent('show', this);
30421     },
30422     
30423     hide: function()
30424     {
30425         if(!this.el){
30426             return;
30427         }
30428         
30429         this.el.hide();
30430         
30431         this.fireEvent('hide', this);
30432     }
30433     
30434 });
30435
30436 Roo.apply(Roo.bootstrap.LocationPicker, {
30437     
30438     OverlayView : function(map, options)
30439     {
30440         options = options || {};
30441         
30442         this.setMap(map);
30443     }
30444     
30445     
30446 });/**
30447  * @class Roo.bootstrap.Alert
30448  * @extends Roo.bootstrap.Component
30449  * Bootstrap Alert class - shows an alert area box
30450  * eg
30451  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30452   Enter a valid email address
30453 </div>
30454  * @licence LGPL
30455  * @cfg {String} title The title of alert
30456  * @cfg {String} html The content of alert
30457  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30458  * @cfg {String} fa font-awesomeicon
30459  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30460  * @cfg {Boolean} close true to show a x closer
30461  * 
30462  * 
30463  * @constructor
30464  * Create a new alert
30465  * @param {Object} config The config object
30466  */
30467
30468
30469 Roo.bootstrap.Alert = function(config){
30470     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30471     
30472 };
30473
30474 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30475     
30476     title: '',
30477     html: '',
30478     weight: false,
30479     fa: false,
30480     faicon: false, // BC
30481     close : false,
30482     
30483     
30484     getAutoCreate : function()
30485     {
30486         
30487         var cfg = {
30488             tag : 'div',
30489             cls : 'alert',
30490             cn : [
30491                 {
30492                     tag: 'button',
30493                     type :  "button",
30494                     cls: "close",
30495                     html : '×',
30496                     style : this.close ? '' : 'display:none'
30497                 },
30498                 {
30499                     tag : 'i',
30500                     cls : 'roo-alert-icon'
30501                     
30502                 },
30503                 {
30504                     tag : 'b',
30505                     cls : 'roo-alert-title',
30506                     html : this.title
30507                 },
30508                 {
30509                     tag : 'span',
30510                     cls : 'roo-alert-text',
30511                     html : this.html
30512                 }
30513             ]
30514         };
30515         
30516         if(this.faicon){
30517             cfg.cn[0].cls += ' fa ' + this.faicon;
30518         }
30519         if(this.fa){
30520             cfg.cn[0].cls += ' fa ' + this.fa;
30521         }
30522         
30523         if(this.weight){
30524             cfg.cls += ' alert-' + this.weight;
30525         }
30526         
30527         return cfg;
30528     },
30529     
30530     initEvents: function() 
30531     {
30532         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30533         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30534         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30535         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30536         if (this.seconds > 0) {
30537             this.hide.defer(this.seconds, this);
30538         }
30539     },
30540     /**
30541      * Set the Title Message HTML
30542      * @param {String} html
30543      */
30544     setTitle : function(str)
30545     {
30546         this.titleEl.dom.innerHTML = str;
30547     },
30548      
30549      /**
30550      * Set the Body Message HTML
30551      * @param {String} html
30552      */
30553     setHtml : function(str)
30554     {
30555         this.htmlEl.dom.innerHTML = str;
30556     },
30557     /**
30558      * Set the Weight of the alert
30559      * @param {String} (success|info|warning|danger) weight
30560      */
30561     
30562     setWeight : function(weight)
30563     {
30564         if(this.weight){
30565             this.el.removeClass('alert-' + this.weight);
30566         }
30567         
30568         this.weight = weight;
30569         
30570         this.el.addClass('alert-' + this.weight);
30571     },
30572       /**
30573      * Set the Icon of the alert
30574      * @param {String} see fontawsome names (name without the 'fa-' bit)
30575      */
30576     setIcon : function(icon)
30577     {
30578         if(this.faicon){
30579             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30580         }
30581         
30582         this.faicon = icon;
30583         
30584         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30585     },
30586     /**
30587      * Hide the Alert
30588      */
30589     hide: function() 
30590     {
30591         this.el.hide();   
30592     },
30593     /**
30594      * Show the Alert
30595      */
30596     show: function() 
30597     {  
30598         this.el.show();   
30599     }
30600     
30601 });
30602
30603  
30604 /*
30605 * Licence: LGPL
30606 */
30607
30608 /**
30609  * @class Roo.bootstrap.UploadCropbox
30610  * @extends Roo.bootstrap.Component
30611  * Bootstrap UploadCropbox class
30612  * @cfg {String} emptyText show when image has been loaded
30613  * @cfg {String} rotateNotify show when image too small to rotate
30614  * @cfg {Number} errorTimeout default 3000
30615  * @cfg {Number} minWidth default 300
30616  * @cfg {Number} minHeight default 300
30617  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30618  * @cfg {Boolean} isDocument (true|false) default false
30619  * @cfg {String} url action url
30620  * @cfg {String} paramName default 'imageUpload'
30621  * @cfg {String} method default POST
30622  * @cfg {Boolean} loadMask (true|false) default true
30623  * @cfg {Boolean} loadingText default 'Loading...'
30624  * 
30625  * @constructor
30626  * Create a new UploadCropbox
30627  * @param {Object} config The config object
30628  */
30629
30630 Roo.bootstrap.UploadCropbox = function(config){
30631     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30632     
30633     this.addEvents({
30634         /**
30635          * @event beforeselectfile
30636          * Fire before select file
30637          * @param {Roo.bootstrap.UploadCropbox} this
30638          */
30639         "beforeselectfile" : true,
30640         /**
30641          * @event initial
30642          * Fire after initEvent
30643          * @param {Roo.bootstrap.UploadCropbox} this
30644          */
30645         "initial" : true,
30646         /**
30647          * @event crop
30648          * Fire after initEvent
30649          * @param {Roo.bootstrap.UploadCropbox} this
30650          * @param {String} data
30651          */
30652         "crop" : true,
30653         /**
30654          * @event prepare
30655          * Fire when preparing the file data
30656          * @param {Roo.bootstrap.UploadCropbox} this
30657          * @param {Object} file
30658          */
30659         "prepare" : true,
30660         /**
30661          * @event exception
30662          * Fire when get exception
30663          * @param {Roo.bootstrap.UploadCropbox} this
30664          * @param {XMLHttpRequest} xhr
30665          */
30666         "exception" : true,
30667         /**
30668          * @event beforeloadcanvas
30669          * Fire before load the canvas
30670          * @param {Roo.bootstrap.UploadCropbox} this
30671          * @param {String} src
30672          */
30673         "beforeloadcanvas" : true,
30674         /**
30675          * @event trash
30676          * Fire when trash image
30677          * @param {Roo.bootstrap.UploadCropbox} this
30678          */
30679         "trash" : true,
30680         /**
30681          * @event download
30682          * Fire when download the image
30683          * @param {Roo.bootstrap.UploadCropbox} this
30684          */
30685         "download" : true,
30686         /**
30687          * @event footerbuttonclick
30688          * Fire when footerbuttonclick
30689          * @param {Roo.bootstrap.UploadCropbox} this
30690          * @param {String} type
30691          */
30692         "footerbuttonclick" : true,
30693         /**
30694          * @event resize
30695          * Fire when resize
30696          * @param {Roo.bootstrap.UploadCropbox} this
30697          */
30698         "resize" : true,
30699         /**
30700          * @event rotate
30701          * Fire when rotate the image
30702          * @param {Roo.bootstrap.UploadCropbox} this
30703          * @param {String} pos
30704          */
30705         "rotate" : true,
30706         /**
30707          * @event inspect
30708          * Fire when inspect the file
30709          * @param {Roo.bootstrap.UploadCropbox} this
30710          * @param {Object} file
30711          */
30712         "inspect" : true,
30713         /**
30714          * @event upload
30715          * Fire when xhr upload the file
30716          * @param {Roo.bootstrap.UploadCropbox} this
30717          * @param {Object} data
30718          */
30719         "upload" : true,
30720         /**
30721          * @event arrange
30722          * Fire when arrange the file data
30723          * @param {Roo.bootstrap.UploadCropbox} this
30724          * @param {Object} formData
30725          */
30726         "arrange" : true
30727     });
30728     
30729     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30730 };
30731
30732 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30733     
30734     emptyText : 'Click to upload image',
30735     rotateNotify : 'Image is too small to rotate',
30736     errorTimeout : 3000,
30737     scale : 0,
30738     baseScale : 1,
30739     rotate : 0,
30740     dragable : false,
30741     pinching : false,
30742     mouseX : 0,
30743     mouseY : 0,
30744     cropData : false,
30745     minWidth : 300,
30746     minHeight : 300,
30747     file : false,
30748     exif : {},
30749     baseRotate : 1,
30750     cropType : 'image/jpeg',
30751     buttons : false,
30752     canvasLoaded : false,
30753     isDocument : false,
30754     method : 'POST',
30755     paramName : 'imageUpload',
30756     loadMask : true,
30757     loadingText : 'Loading...',
30758     maskEl : false,
30759     
30760     getAutoCreate : function()
30761     {
30762         var cfg = {
30763             tag : 'div',
30764             cls : 'roo-upload-cropbox',
30765             cn : [
30766                 {
30767                     tag : 'input',
30768                     cls : 'roo-upload-cropbox-selector',
30769                     type : 'file'
30770                 },
30771                 {
30772                     tag : 'div',
30773                     cls : 'roo-upload-cropbox-body',
30774                     style : 'cursor:pointer',
30775                     cn : [
30776                         {
30777                             tag : 'div',
30778                             cls : 'roo-upload-cropbox-preview'
30779                         },
30780                         {
30781                             tag : 'div',
30782                             cls : 'roo-upload-cropbox-thumb'
30783                         },
30784                         {
30785                             tag : 'div',
30786                             cls : 'roo-upload-cropbox-empty-notify',
30787                             html : this.emptyText
30788                         },
30789                         {
30790                             tag : 'div',
30791                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30792                             html : this.rotateNotify
30793                         }
30794                     ]
30795                 },
30796                 {
30797                     tag : 'div',
30798                     cls : 'roo-upload-cropbox-footer',
30799                     cn : {
30800                         tag : 'div',
30801                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30802                         cn : []
30803                     }
30804                 }
30805             ]
30806         };
30807         
30808         return cfg;
30809     },
30810     
30811     onRender : function(ct, position)
30812     {
30813         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30814         
30815         if (this.buttons.length) {
30816             
30817             Roo.each(this.buttons, function(bb) {
30818                 
30819                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30820                 
30821                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30822                 
30823             }, this);
30824         }
30825         
30826         if(this.loadMask){
30827             this.maskEl = this.el;
30828         }
30829     },
30830     
30831     initEvents : function()
30832     {
30833         this.urlAPI = (window.createObjectURL && window) || 
30834                                 (window.URL && URL.revokeObjectURL && URL) || 
30835                                 (window.webkitURL && webkitURL);
30836                         
30837         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30838         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30839         
30840         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30841         this.selectorEl.hide();
30842         
30843         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30844         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30845         
30846         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30847         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30848         this.thumbEl.hide();
30849         
30850         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30851         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30852         
30853         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30854         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30855         this.errorEl.hide();
30856         
30857         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30858         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30859         this.footerEl.hide();
30860         
30861         this.setThumbBoxSize();
30862         
30863         this.bind();
30864         
30865         this.resize();
30866         
30867         this.fireEvent('initial', this);
30868     },
30869
30870     bind : function()
30871     {
30872         var _this = this;
30873         
30874         window.addEventListener("resize", function() { _this.resize(); } );
30875         
30876         this.bodyEl.on('click', this.beforeSelectFile, this);
30877         
30878         if(Roo.isTouch){
30879             this.bodyEl.on('touchstart', this.onTouchStart, this);
30880             this.bodyEl.on('touchmove', this.onTouchMove, this);
30881             this.bodyEl.on('touchend', this.onTouchEnd, this);
30882         }
30883         
30884         if(!Roo.isTouch){
30885             this.bodyEl.on('mousedown', this.onMouseDown, this);
30886             this.bodyEl.on('mousemove', this.onMouseMove, this);
30887             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30888             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30889             Roo.get(document).on('mouseup', this.onMouseUp, this);
30890         }
30891         
30892         this.selectorEl.on('change', this.onFileSelected, this);
30893     },
30894     
30895     reset : function()
30896     {    
30897         this.scale = 0;
30898         this.baseScale = 1;
30899         this.rotate = 0;
30900         this.baseRotate = 1;
30901         this.dragable = false;
30902         this.pinching = false;
30903         this.mouseX = 0;
30904         this.mouseY = 0;
30905         this.cropData = false;
30906         this.notifyEl.dom.innerHTML = this.emptyText;
30907         
30908         this.selectorEl.dom.value = '';
30909         
30910     },
30911     
30912     resize : function()
30913     {
30914         if(this.fireEvent('resize', this) != false){
30915             this.setThumbBoxPosition();
30916             this.setCanvasPosition();
30917         }
30918     },
30919     
30920     onFooterButtonClick : function(e, el, o, type)
30921     {
30922         switch (type) {
30923             case 'rotate-left' :
30924                 this.onRotateLeft(e);
30925                 break;
30926             case 'rotate-right' :
30927                 this.onRotateRight(e);
30928                 break;
30929             case 'picture' :
30930                 this.beforeSelectFile(e);
30931                 break;
30932             case 'trash' :
30933                 this.trash(e);
30934                 break;
30935             case 'crop' :
30936                 this.crop(e);
30937                 break;
30938             case 'download' :
30939                 this.download(e);
30940                 break;
30941             default :
30942                 break;
30943         }
30944         
30945         this.fireEvent('footerbuttonclick', this, type);
30946     },
30947     
30948     beforeSelectFile : function(e)
30949     {
30950         e.preventDefault();
30951         
30952         if(this.fireEvent('beforeselectfile', this) != false){
30953             this.selectorEl.dom.click();
30954         }
30955     },
30956     
30957     onFileSelected : function(e)
30958     {
30959         e.preventDefault();
30960         
30961         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30962             return;
30963         }
30964         
30965         var file = this.selectorEl.dom.files[0];
30966         
30967         if(this.fireEvent('inspect', this, file) != false){
30968             this.prepare(file);
30969         }
30970         
30971     },
30972     
30973     trash : function(e)
30974     {
30975         this.fireEvent('trash', this);
30976     },
30977     
30978     download : function(e)
30979     {
30980         this.fireEvent('download', this);
30981     },
30982     
30983     loadCanvas : function(src)
30984     {   
30985         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30986             
30987             this.reset();
30988             
30989             this.imageEl = document.createElement('img');
30990             
30991             var _this = this;
30992             
30993             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30994             
30995             this.imageEl.src = src;
30996         }
30997     },
30998     
30999     onLoadCanvas : function()
31000     {   
31001         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31002         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31003         
31004         this.bodyEl.un('click', this.beforeSelectFile, this);
31005         
31006         this.notifyEl.hide();
31007         this.thumbEl.show();
31008         this.footerEl.show();
31009         
31010         this.baseRotateLevel();
31011         
31012         if(this.isDocument){
31013             this.setThumbBoxSize();
31014         }
31015         
31016         this.setThumbBoxPosition();
31017         
31018         this.baseScaleLevel();
31019         
31020         this.draw();
31021         
31022         this.resize();
31023         
31024         this.canvasLoaded = true;
31025         
31026         if(this.loadMask){
31027             this.maskEl.unmask();
31028         }
31029         
31030     },
31031     
31032     setCanvasPosition : function()
31033     {   
31034         if(!this.canvasEl){
31035             return;
31036         }
31037         
31038         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31039         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31040         
31041         this.previewEl.setLeft(pw);
31042         this.previewEl.setTop(ph);
31043         
31044     },
31045     
31046     onMouseDown : function(e)
31047     {   
31048         e.stopEvent();
31049         
31050         this.dragable = true;
31051         this.pinching = false;
31052         
31053         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31054             this.dragable = false;
31055             return;
31056         }
31057         
31058         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31059         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31060         
31061     },
31062     
31063     onMouseMove : function(e)
31064     {   
31065         e.stopEvent();
31066         
31067         if(!this.canvasLoaded){
31068             return;
31069         }
31070         
31071         if (!this.dragable){
31072             return;
31073         }
31074         
31075         var minX = Math.ceil(this.thumbEl.getLeft(true));
31076         var minY = Math.ceil(this.thumbEl.getTop(true));
31077         
31078         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31079         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31080         
31081         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31082         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31083         
31084         x = x - this.mouseX;
31085         y = y - this.mouseY;
31086         
31087         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31088         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31089         
31090         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31091         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31092         
31093         this.previewEl.setLeft(bgX);
31094         this.previewEl.setTop(bgY);
31095         
31096         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31097         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31098     },
31099     
31100     onMouseUp : function(e)
31101     {   
31102         e.stopEvent();
31103         
31104         this.dragable = false;
31105     },
31106     
31107     onMouseWheel : function(e)
31108     {   
31109         e.stopEvent();
31110         
31111         this.startScale = this.scale;
31112         
31113         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31114         
31115         if(!this.zoomable()){
31116             this.scale = this.startScale;
31117             return;
31118         }
31119         
31120         this.draw();
31121         
31122         return;
31123     },
31124     
31125     zoomable : function()
31126     {
31127         var minScale = this.thumbEl.getWidth() / this.minWidth;
31128         
31129         if(this.minWidth < this.minHeight){
31130             minScale = this.thumbEl.getHeight() / this.minHeight;
31131         }
31132         
31133         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31134         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31135         
31136         if(
31137                 this.isDocument &&
31138                 (this.rotate == 0 || this.rotate == 180) && 
31139                 (
31140                     width > this.imageEl.OriginWidth || 
31141                     height > this.imageEl.OriginHeight ||
31142                     (width < this.minWidth && height < this.minHeight)
31143                 )
31144         ){
31145             return false;
31146         }
31147         
31148         if(
31149                 this.isDocument &&
31150                 (this.rotate == 90 || this.rotate == 270) && 
31151                 (
31152                     width > this.imageEl.OriginWidth || 
31153                     height > this.imageEl.OriginHeight ||
31154                     (width < this.minHeight && height < this.minWidth)
31155                 )
31156         ){
31157             return false;
31158         }
31159         
31160         if(
31161                 !this.isDocument &&
31162                 (this.rotate == 0 || this.rotate == 180) && 
31163                 (
31164                     width < this.minWidth || 
31165                     width > this.imageEl.OriginWidth || 
31166                     height < this.minHeight || 
31167                     height > this.imageEl.OriginHeight
31168                 )
31169         ){
31170             return false;
31171         }
31172         
31173         if(
31174                 !this.isDocument &&
31175                 (this.rotate == 90 || this.rotate == 270) && 
31176                 (
31177                     width < this.minHeight || 
31178                     width > this.imageEl.OriginWidth || 
31179                     height < this.minWidth || 
31180                     height > this.imageEl.OriginHeight
31181                 )
31182         ){
31183             return false;
31184         }
31185         
31186         return true;
31187         
31188     },
31189     
31190     onRotateLeft : function(e)
31191     {   
31192         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31193             
31194             var minScale = this.thumbEl.getWidth() / this.minWidth;
31195             
31196             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31197             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31198             
31199             this.startScale = this.scale;
31200             
31201             while (this.getScaleLevel() < minScale){
31202             
31203                 this.scale = this.scale + 1;
31204                 
31205                 if(!this.zoomable()){
31206                     break;
31207                 }
31208                 
31209                 if(
31210                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31211                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31212                 ){
31213                     continue;
31214                 }
31215                 
31216                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31217
31218                 this.draw();
31219                 
31220                 return;
31221             }
31222             
31223             this.scale = this.startScale;
31224             
31225             this.onRotateFail();
31226             
31227             return false;
31228         }
31229         
31230         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31231
31232         if(this.isDocument){
31233             this.setThumbBoxSize();
31234             this.setThumbBoxPosition();
31235             this.setCanvasPosition();
31236         }
31237         
31238         this.draw();
31239         
31240         this.fireEvent('rotate', this, 'left');
31241         
31242     },
31243     
31244     onRotateRight : function(e)
31245     {
31246         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31247             
31248             var minScale = this.thumbEl.getWidth() / this.minWidth;
31249         
31250             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31251             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31252             
31253             this.startScale = this.scale;
31254             
31255             while (this.getScaleLevel() < minScale){
31256             
31257                 this.scale = this.scale + 1;
31258                 
31259                 if(!this.zoomable()){
31260                     break;
31261                 }
31262                 
31263                 if(
31264                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31265                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31266                 ){
31267                     continue;
31268                 }
31269                 
31270                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31271
31272                 this.draw();
31273                 
31274                 return;
31275             }
31276             
31277             this.scale = this.startScale;
31278             
31279             this.onRotateFail();
31280             
31281             return false;
31282         }
31283         
31284         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31285
31286         if(this.isDocument){
31287             this.setThumbBoxSize();
31288             this.setThumbBoxPosition();
31289             this.setCanvasPosition();
31290         }
31291         
31292         this.draw();
31293         
31294         this.fireEvent('rotate', this, 'right');
31295     },
31296     
31297     onRotateFail : function()
31298     {
31299         this.errorEl.show(true);
31300         
31301         var _this = this;
31302         
31303         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31304     },
31305     
31306     draw : function()
31307     {
31308         this.previewEl.dom.innerHTML = '';
31309         
31310         var canvasEl = document.createElement("canvas");
31311         
31312         var contextEl = canvasEl.getContext("2d");
31313         
31314         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31315         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31316         var center = this.imageEl.OriginWidth / 2;
31317         
31318         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31319             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31320             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31321             center = this.imageEl.OriginHeight / 2;
31322         }
31323         
31324         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31325         
31326         contextEl.translate(center, center);
31327         contextEl.rotate(this.rotate * Math.PI / 180);
31328
31329         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31330         
31331         this.canvasEl = document.createElement("canvas");
31332         
31333         this.contextEl = this.canvasEl.getContext("2d");
31334         
31335         switch (this.rotate) {
31336             case 0 :
31337                 
31338                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31339                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31340                 
31341                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31342                 
31343                 break;
31344             case 90 : 
31345                 
31346                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31347                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31348                 
31349                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31350                     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);
31351                     break;
31352                 }
31353                 
31354                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31355                 
31356                 break;
31357             case 180 :
31358                 
31359                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31360                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31361                 
31362                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31363                     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);
31364                     break;
31365                 }
31366                 
31367                 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);
31368                 
31369                 break;
31370             case 270 :
31371                 
31372                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31373                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31374         
31375                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31376                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31377                     break;
31378                 }
31379                 
31380                 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);
31381                 
31382                 break;
31383             default : 
31384                 break;
31385         }
31386         
31387         this.previewEl.appendChild(this.canvasEl);
31388         
31389         this.setCanvasPosition();
31390     },
31391     
31392     crop : function()
31393     {
31394         if(!this.canvasLoaded){
31395             return;
31396         }
31397         
31398         var imageCanvas = document.createElement("canvas");
31399         
31400         var imageContext = imageCanvas.getContext("2d");
31401         
31402         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31403         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31404         
31405         var center = imageCanvas.width / 2;
31406         
31407         imageContext.translate(center, center);
31408         
31409         imageContext.rotate(this.rotate * Math.PI / 180);
31410         
31411         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31412         
31413         var canvas = document.createElement("canvas");
31414         
31415         var context = canvas.getContext("2d");
31416                 
31417         canvas.width = this.minWidth;
31418         canvas.height = this.minHeight;
31419
31420         switch (this.rotate) {
31421             case 0 :
31422                 
31423                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31424                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31425                 
31426                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31427                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31428                 
31429                 var targetWidth = this.minWidth - 2 * x;
31430                 var targetHeight = this.minHeight - 2 * y;
31431                 
31432                 var scale = 1;
31433                 
31434                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31435                     scale = targetWidth / width;
31436                 }
31437                 
31438                 if(x > 0 && y == 0){
31439                     scale = targetHeight / height;
31440                 }
31441                 
31442                 if(x > 0 && y > 0){
31443                     scale = targetWidth / width;
31444                     
31445                     if(width < height){
31446                         scale = targetHeight / height;
31447                     }
31448                 }
31449                 
31450                 context.scale(scale, scale);
31451                 
31452                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31453                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31454
31455                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31456                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31457
31458                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31459                 
31460                 break;
31461             case 90 : 
31462                 
31463                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31464                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31465                 
31466                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31467                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31468                 
31469                 var targetWidth = this.minWidth - 2 * x;
31470                 var targetHeight = this.minHeight - 2 * y;
31471                 
31472                 var scale = 1;
31473                 
31474                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31475                     scale = targetWidth / width;
31476                 }
31477                 
31478                 if(x > 0 && y == 0){
31479                     scale = targetHeight / height;
31480                 }
31481                 
31482                 if(x > 0 && y > 0){
31483                     scale = targetWidth / width;
31484                     
31485                     if(width < height){
31486                         scale = targetHeight / height;
31487                     }
31488                 }
31489                 
31490                 context.scale(scale, scale);
31491                 
31492                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31493                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31494
31495                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31496                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31497                 
31498                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31499                 
31500                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31501                 
31502                 break;
31503             case 180 :
31504                 
31505                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31506                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31507                 
31508                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31509                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31510                 
31511                 var targetWidth = this.minWidth - 2 * x;
31512                 var targetHeight = this.minHeight - 2 * y;
31513                 
31514                 var scale = 1;
31515                 
31516                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31517                     scale = targetWidth / width;
31518                 }
31519                 
31520                 if(x > 0 && y == 0){
31521                     scale = targetHeight / height;
31522                 }
31523                 
31524                 if(x > 0 && y > 0){
31525                     scale = targetWidth / width;
31526                     
31527                     if(width < height){
31528                         scale = targetHeight / height;
31529                     }
31530                 }
31531                 
31532                 context.scale(scale, scale);
31533                 
31534                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31535                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31536
31537                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31538                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31539
31540                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31541                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31542                 
31543                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31544                 
31545                 break;
31546             case 270 :
31547                 
31548                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31549                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31550                 
31551                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31552                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31553                 
31554                 var targetWidth = this.minWidth - 2 * x;
31555                 var targetHeight = this.minHeight - 2 * y;
31556                 
31557                 var scale = 1;
31558                 
31559                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31560                     scale = targetWidth / width;
31561                 }
31562                 
31563                 if(x > 0 && y == 0){
31564                     scale = targetHeight / height;
31565                 }
31566                 
31567                 if(x > 0 && y > 0){
31568                     scale = targetWidth / width;
31569                     
31570                     if(width < height){
31571                         scale = targetHeight / height;
31572                     }
31573                 }
31574                 
31575                 context.scale(scale, scale);
31576                 
31577                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31578                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31579
31580                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31581                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31582                 
31583                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31584                 
31585                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31586                 
31587                 break;
31588             default : 
31589                 break;
31590         }
31591         
31592         this.cropData = canvas.toDataURL(this.cropType);
31593         
31594         if(this.fireEvent('crop', this, this.cropData) !== false){
31595             this.process(this.file, this.cropData);
31596         }
31597         
31598         return;
31599         
31600     },
31601     
31602     setThumbBoxSize : function()
31603     {
31604         var width, height;
31605         
31606         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31607             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31608             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31609             
31610             this.minWidth = width;
31611             this.minHeight = height;
31612             
31613             if(this.rotate == 90 || this.rotate == 270){
31614                 this.minWidth = height;
31615                 this.minHeight = width;
31616             }
31617         }
31618         
31619         height = 300;
31620         width = Math.ceil(this.minWidth * height / this.minHeight);
31621         
31622         if(this.minWidth > this.minHeight){
31623             width = 300;
31624             height = Math.ceil(this.minHeight * width / this.minWidth);
31625         }
31626         
31627         this.thumbEl.setStyle({
31628             width : width + 'px',
31629             height : height + 'px'
31630         });
31631
31632         return;
31633             
31634     },
31635     
31636     setThumbBoxPosition : function()
31637     {
31638         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31639         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31640         
31641         this.thumbEl.setLeft(x);
31642         this.thumbEl.setTop(y);
31643         
31644     },
31645     
31646     baseRotateLevel : function()
31647     {
31648         this.baseRotate = 1;
31649         
31650         if(
31651                 typeof(this.exif) != 'undefined' &&
31652                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31653                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31654         ){
31655             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31656         }
31657         
31658         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31659         
31660     },
31661     
31662     baseScaleLevel : function()
31663     {
31664         var width, height;
31665         
31666         if(this.isDocument){
31667             
31668             if(this.baseRotate == 6 || this.baseRotate == 8){
31669             
31670                 height = this.thumbEl.getHeight();
31671                 this.baseScale = height / this.imageEl.OriginWidth;
31672
31673                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31674                     width = this.thumbEl.getWidth();
31675                     this.baseScale = width / this.imageEl.OriginHeight;
31676                 }
31677
31678                 return;
31679             }
31680
31681             height = this.thumbEl.getHeight();
31682             this.baseScale = height / this.imageEl.OriginHeight;
31683
31684             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31685                 width = this.thumbEl.getWidth();
31686                 this.baseScale = width / this.imageEl.OriginWidth;
31687             }
31688
31689             return;
31690         }
31691         
31692         if(this.baseRotate == 6 || this.baseRotate == 8){
31693             
31694             width = this.thumbEl.getHeight();
31695             this.baseScale = width / this.imageEl.OriginHeight;
31696             
31697             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31698                 height = this.thumbEl.getWidth();
31699                 this.baseScale = height / this.imageEl.OriginHeight;
31700             }
31701             
31702             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31703                 height = this.thumbEl.getWidth();
31704                 this.baseScale = height / this.imageEl.OriginHeight;
31705                 
31706                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31707                     width = this.thumbEl.getHeight();
31708                     this.baseScale = width / this.imageEl.OriginWidth;
31709                 }
31710             }
31711             
31712             return;
31713         }
31714         
31715         width = this.thumbEl.getWidth();
31716         this.baseScale = width / this.imageEl.OriginWidth;
31717         
31718         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31719             height = this.thumbEl.getHeight();
31720             this.baseScale = height / this.imageEl.OriginHeight;
31721         }
31722         
31723         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31724             
31725             height = this.thumbEl.getHeight();
31726             this.baseScale = height / this.imageEl.OriginHeight;
31727             
31728             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31729                 width = this.thumbEl.getWidth();
31730                 this.baseScale = width / this.imageEl.OriginWidth;
31731             }
31732             
31733         }
31734         
31735         return;
31736     },
31737     
31738     getScaleLevel : function()
31739     {
31740         return this.baseScale * Math.pow(1.1, this.scale);
31741     },
31742     
31743     onTouchStart : function(e)
31744     {
31745         if(!this.canvasLoaded){
31746             this.beforeSelectFile(e);
31747             return;
31748         }
31749         
31750         var touches = e.browserEvent.touches;
31751         
31752         if(!touches){
31753             return;
31754         }
31755         
31756         if(touches.length == 1){
31757             this.onMouseDown(e);
31758             return;
31759         }
31760         
31761         if(touches.length != 2){
31762             return;
31763         }
31764         
31765         var coords = [];
31766         
31767         for(var i = 0, finger; finger = touches[i]; i++){
31768             coords.push(finger.pageX, finger.pageY);
31769         }
31770         
31771         var x = Math.pow(coords[0] - coords[2], 2);
31772         var y = Math.pow(coords[1] - coords[3], 2);
31773         
31774         this.startDistance = Math.sqrt(x + y);
31775         
31776         this.startScale = this.scale;
31777         
31778         this.pinching = true;
31779         this.dragable = false;
31780         
31781     },
31782     
31783     onTouchMove : function(e)
31784     {
31785         if(!this.pinching && !this.dragable){
31786             return;
31787         }
31788         
31789         var touches = e.browserEvent.touches;
31790         
31791         if(!touches){
31792             return;
31793         }
31794         
31795         if(this.dragable){
31796             this.onMouseMove(e);
31797             return;
31798         }
31799         
31800         var coords = [];
31801         
31802         for(var i = 0, finger; finger = touches[i]; i++){
31803             coords.push(finger.pageX, finger.pageY);
31804         }
31805         
31806         var x = Math.pow(coords[0] - coords[2], 2);
31807         var y = Math.pow(coords[1] - coords[3], 2);
31808         
31809         this.endDistance = Math.sqrt(x + y);
31810         
31811         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31812         
31813         if(!this.zoomable()){
31814             this.scale = this.startScale;
31815             return;
31816         }
31817         
31818         this.draw();
31819         
31820     },
31821     
31822     onTouchEnd : function(e)
31823     {
31824         this.pinching = false;
31825         this.dragable = false;
31826         
31827     },
31828     
31829     process : function(file, crop)
31830     {
31831         if(this.loadMask){
31832             this.maskEl.mask(this.loadingText);
31833         }
31834         
31835         this.xhr = new XMLHttpRequest();
31836         
31837         file.xhr = this.xhr;
31838
31839         this.xhr.open(this.method, this.url, true);
31840         
31841         var headers = {
31842             "Accept": "application/json",
31843             "Cache-Control": "no-cache",
31844             "X-Requested-With": "XMLHttpRequest"
31845         };
31846         
31847         for (var headerName in headers) {
31848             var headerValue = headers[headerName];
31849             if (headerValue) {
31850                 this.xhr.setRequestHeader(headerName, headerValue);
31851             }
31852         }
31853         
31854         var _this = this;
31855         
31856         this.xhr.onload = function()
31857         {
31858             _this.xhrOnLoad(_this.xhr);
31859         }
31860         
31861         this.xhr.onerror = function()
31862         {
31863             _this.xhrOnError(_this.xhr);
31864         }
31865         
31866         var formData = new FormData();
31867
31868         formData.append('returnHTML', 'NO');
31869         
31870         if(crop){
31871             formData.append('crop', crop);
31872         }
31873         
31874         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31875             formData.append(this.paramName, file, file.name);
31876         }
31877         
31878         if(typeof(file.filename) != 'undefined'){
31879             formData.append('filename', file.filename);
31880         }
31881         
31882         if(typeof(file.mimetype) != 'undefined'){
31883             formData.append('mimetype', file.mimetype);
31884         }
31885         
31886         if(this.fireEvent('arrange', this, formData) != false){
31887             this.xhr.send(formData);
31888         };
31889     },
31890     
31891     xhrOnLoad : function(xhr)
31892     {
31893         if(this.loadMask){
31894             this.maskEl.unmask();
31895         }
31896         
31897         if (xhr.readyState !== 4) {
31898             this.fireEvent('exception', this, xhr);
31899             return;
31900         }
31901
31902         var response = Roo.decode(xhr.responseText);
31903         
31904         if(!response.success){
31905             this.fireEvent('exception', this, xhr);
31906             return;
31907         }
31908         
31909         var response = Roo.decode(xhr.responseText);
31910         
31911         this.fireEvent('upload', this, response);
31912         
31913     },
31914     
31915     xhrOnError : function()
31916     {
31917         if(this.loadMask){
31918             this.maskEl.unmask();
31919         }
31920         
31921         Roo.log('xhr on error');
31922         
31923         var response = Roo.decode(xhr.responseText);
31924           
31925         Roo.log(response);
31926         
31927     },
31928     
31929     prepare : function(file)
31930     {   
31931         if(this.loadMask){
31932             this.maskEl.mask(this.loadingText);
31933         }
31934         
31935         this.file = false;
31936         this.exif = {};
31937         
31938         if(typeof(file) === 'string'){
31939             this.loadCanvas(file);
31940             return;
31941         }
31942         
31943         if(!file || !this.urlAPI){
31944             return;
31945         }
31946         
31947         this.file = file;
31948         this.cropType = file.type;
31949         
31950         var _this = this;
31951         
31952         if(this.fireEvent('prepare', this, this.file) != false){
31953             
31954             var reader = new FileReader();
31955             
31956             reader.onload = function (e) {
31957                 if (e.target.error) {
31958                     Roo.log(e.target.error);
31959                     return;
31960                 }
31961                 
31962                 var buffer = e.target.result,
31963                     dataView = new DataView(buffer),
31964                     offset = 2,
31965                     maxOffset = dataView.byteLength - 4,
31966                     markerBytes,
31967                     markerLength;
31968                 
31969                 if (dataView.getUint16(0) === 0xffd8) {
31970                     while (offset < maxOffset) {
31971                         markerBytes = dataView.getUint16(offset);
31972                         
31973                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31974                             markerLength = dataView.getUint16(offset + 2) + 2;
31975                             if (offset + markerLength > dataView.byteLength) {
31976                                 Roo.log('Invalid meta data: Invalid segment size.');
31977                                 break;
31978                             }
31979                             
31980                             if(markerBytes == 0xffe1){
31981                                 _this.parseExifData(
31982                                     dataView,
31983                                     offset,
31984                                     markerLength
31985                                 );
31986                             }
31987                             
31988                             offset += markerLength;
31989                             
31990                             continue;
31991                         }
31992                         
31993                         break;
31994                     }
31995                     
31996                 }
31997                 
31998                 var url = _this.urlAPI.createObjectURL(_this.file);
31999                 
32000                 _this.loadCanvas(url);
32001                 
32002                 return;
32003             }
32004             
32005             reader.readAsArrayBuffer(this.file);
32006             
32007         }
32008         
32009     },
32010     
32011     parseExifData : function(dataView, offset, length)
32012     {
32013         var tiffOffset = offset + 10,
32014             littleEndian,
32015             dirOffset;
32016     
32017         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32018             // No Exif data, might be XMP data instead
32019             return;
32020         }
32021         
32022         // Check for the ASCII code for "Exif" (0x45786966):
32023         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32024             // No Exif data, might be XMP data instead
32025             return;
32026         }
32027         if (tiffOffset + 8 > dataView.byteLength) {
32028             Roo.log('Invalid Exif data: Invalid segment size.');
32029             return;
32030         }
32031         // Check for the two null bytes:
32032         if (dataView.getUint16(offset + 8) !== 0x0000) {
32033             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32034             return;
32035         }
32036         // Check the byte alignment:
32037         switch (dataView.getUint16(tiffOffset)) {
32038         case 0x4949:
32039             littleEndian = true;
32040             break;
32041         case 0x4D4D:
32042             littleEndian = false;
32043             break;
32044         default:
32045             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32046             return;
32047         }
32048         // Check for the TIFF tag marker (0x002A):
32049         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32050             Roo.log('Invalid Exif data: Missing TIFF marker.');
32051             return;
32052         }
32053         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32054         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32055         
32056         this.parseExifTags(
32057             dataView,
32058             tiffOffset,
32059             tiffOffset + dirOffset,
32060             littleEndian
32061         );
32062     },
32063     
32064     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32065     {
32066         var tagsNumber,
32067             dirEndOffset,
32068             i;
32069         if (dirOffset + 6 > dataView.byteLength) {
32070             Roo.log('Invalid Exif data: Invalid directory offset.');
32071             return;
32072         }
32073         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32074         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32075         if (dirEndOffset + 4 > dataView.byteLength) {
32076             Roo.log('Invalid Exif data: Invalid directory size.');
32077             return;
32078         }
32079         for (i = 0; i < tagsNumber; i += 1) {
32080             this.parseExifTag(
32081                 dataView,
32082                 tiffOffset,
32083                 dirOffset + 2 + 12 * i, // tag offset
32084                 littleEndian
32085             );
32086         }
32087         // Return the offset to the next directory:
32088         return dataView.getUint32(dirEndOffset, littleEndian);
32089     },
32090     
32091     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32092     {
32093         var tag = dataView.getUint16(offset, littleEndian);
32094         
32095         this.exif[tag] = this.getExifValue(
32096             dataView,
32097             tiffOffset,
32098             offset,
32099             dataView.getUint16(offset + 2, littleEndian), // tag type
32100             dataView.getUint32(offset + 4, littleEndian), // tag length
32101             littleEndian
32102         );
32103     },
32104     
32105     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32106     {
32107         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32108             tagSize,
32109             dataOffset,
32110             values,
32111             i,
32112             str,
32113             c;
32114     
32115         if (!tagType) {
32116             Roo.log('Invalid Exif data: Invalid tag type.');
32117             return;
32118         }
32119         
32120         tagSize = tagType.size * length;
32121         // Determine if the value is contained in the dataOffset bytes,
32122         // or if the value at the dataOffset is a pointer to the actual data:
32123         dataOffset = tagSize > 4 ?
32124                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32125         if (dataOffset + tagSize > dataView.byteLength) {
32126             Roo.log('Invalid Exif data: Invalid data offset.');
32127             return;
32128         }
32129         if (length === 1) {
32130             return tagType.getValue(dataView, dataOffset, littleEndian);
32131         }
32132         values = [];
32133         for (i = 0; i < length; i += 1) {
32134             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32135         }
32136         
32137         if (tagType.ascii) {
32138             str = '';
32139             // Concatenate the chars:
32140             for (i = 0; i < values.length; i += 1) {
32141                 c = values[i];
32142                 // Ignore the terminating NULL byte(s):
32143                 if (c === '\u0000') {
32144                     break;
32145                 }
32146                 str += c;
32147             }
32148             return str;
32149         }
32150         return values;
32151     }
32152     
32153 });
32154
32155 Roo.apply(Roo.bootstrap.UploadCropbox, {
32156     tags : {
32157         'Orientation': 0x0112
32158     },
32159     
32160     Orientation: {
32161             1: 0, //'top-left',
32162 //            2: 'top-right',
32163             3: 180, //'bottom-right',
32164 //            4: 'bottom-left',
32165 //            5: 'left-top',
32166             6: 90, //'right-top',
32167 //            7: 'right-bottom',
32168             8: 270 //'left-bottom'
32169     },
32170     
32171     exifTagTypes : {
32172         // byte, 8-bit unsigned int:
32173         1: {
32174             getValue: function (dataView, dataOffset) {
32175                 return dataView.getUint8(dataOffset);
32176             },
32177             size: 1
32178         },
32179         // ascii, 8-bit byte:
32180         2: {
32181             getValue: function (dataView, dataOffset) {
32182                 return String.fromCharCode(dataView.getUint8(dataOffset));
32183             },
32184             size: 1,
32185             ascii: true
32186         },
32187         // short, 16 bit int:
32188         3: {
32189             getValue: function (dataView, dataOffset, littleEndian) {
32190                 return dataView.getUint16(dataOffset, littleEndian);
32191             },
32192             size: 2
32193         },
32194         // long, 32 bit int:
32195         4: {
32196             getValue: function (dataView, dataOffset, littleEndian) {
32197                 return dataView.getUint32(dataOffset, littleEndian);
32198             },
32199             size: 4
32200         },
32201         // rational = two long values, first is numerator, second is denominator:
32202         5: {
32203             getValue: function (dataView, dataOffset, littleEndian) {
32204                 return dataView.getUint32(dataOffset, littleEndian) /
32205                     dataView.getUint32(dataOffset + 4, littleEndian);
32206             },
32207             size: 8
32208         },
32209         // slong, 32 bit signed int:
32210         9: {
32211             getValue: function (dataView, dataOffset, littleEndian) {
32212                 return dataView.getInt32(dataOffset, littleEndian);
32213             },
32214             size: 4
32215         },
32216         // srational, two slongs, first is numerator, second is denominator:
32217         10: {
32218             getValue: function (dataView, dataOffset, littleEndian) {
32219                 return dataView.getInt32(dataOffset, littleEndian) /
32220                     dataView.getInt32(dataOffset + 4, littleEndian);
32221             },
32222             size: 8
32223         }
32224     },
32225     
32226     footer : {
32227         STANDARD : [
32228             {
32229                 tag : 'div',
32230                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32231                 action : 'rotate-left',
32232                 cn : [
32233                     {
32234                         tag : 'button',
32235                         cls : 'btn btn-default',
32236                         html : '<i class="fa fa-undo"></i>'
32237                     }
32238                 ]
32239             },
32240             {
32241                 tag : 'div',
32242                 cls : 'btn-group roo-upload-cropbox-picture',
32243                 action : 'picture',
32244                 cn : [
32245                     {
32246                         tag : 'button',
32247                         cls : 'btn btn-default',
32248                         html : '<i class="fa fa-picture-o"></i>'
32249                     }
32250                 ]
32251             },
32252             {
32253                 tag : 'div',
32254                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32255                 action : 'rotate-right',
32256                 cn : [
32257                     {
32258                         tag : 'button',
32259                         cls : 'btn btn-default',
32260                         html : '<i class="fa fa-repeat"></i>'
32261                     }
32262                 ]
32263             }
32264         ],
32265         DOCUMENT : [
32266             {
32267                 tag : 'div',
32268                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32269                 action : 'rotate-left',
32270                 cn : [
32271                     {
32272                         tag : 'button',
32273                         cls : 'btn btn-default',
32274                         html : '<i class="fa fa-undo"></i>'
32275                     }
32276                 ]
32277             },
32278             {
32279                 tag : 'div',
32280                 cls : 'btn-group roo-upload-cropbox-download',
32281                 action : 'download',
32282                 cn : [
32283                     {
32284                         tag : 'button',
32285                         cls : 'btn btn-default',
32286                         html : '<i class="fa fa-download"></i>'
32287                     }
32288                 ]
32289             },
32290             {
32291                 tag : 'div',
32292                 cls : 'btn-group roo-upload-cropbox-crop',
32293                 action : 'crop',
32294                 cn : [
32295                     {
32296                         tag : 'button',
32297                         cls : 'btn btn-default',
32298                         html : '<i class="fa fa-crop"></i>'
32299                     }
32300                 ]
32301             },
32302             {
32303                 tag : 'div',
32304                 cls : 'btn-group roo-upload-cropbox-trash',
32305                 action : 'trash',
32306                 cn : [
32307                     {
32308                         tag : 'button',
32309                         cls : 'btn btn-default',
32310                         html : '<i class="fa fa-trash"></i>'
32311                     }
32312                 ]
32313             },
32314             {
32315                 tag : 'div',
32316                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32317                 action : 'rotate-right',
32318                 cn : [
32319                     {
32320                         tag : 'button',
32321                         cls : 'btn btn-default',
32322                         html : '<i class="fa fa-repeat"></i>'
32323                     }
32324                 ]
32325             }
32326         ],
32327         ROTATOR : [
32328             {
32329                 tag : 'div',
32330                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32331                 action : 'rotate-left',
32332                 cn : [
32333                     {
32334                         tag : 'button',
32335                         cls : 'btn btn-default',
32336                         html : '<i class="fa fa-undo"></i>'
32337                     }
32338                 ]
32339             },
32340             {
32341                 tag : 'div',
32342                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32343                 action : 'rotate-right',
32344                 cn : [
32345                     {
32346                         tag : 'button',
32347                         cls : 'btn btn-default',
32348                         html : '<i class="fa fa-repeat"></i>'
32349                     }
32350                 ]
32351             }
32352         ]
32353     }
32354 });
32355
32356 /*
32357 * Licence: LGPL
32358 */
32359
32360 /**
32361  * @class Roo.bootstrap.DocumentManager
32362  * @extends Roo.bootstrap.Component
32363  * Bootstrap DocumentManager class
32364  * @cfg {String} paramName default 'imageUpload'
32365  * @cfg {String} toolTipName default 'filename'
32366  * @cfg {String} method default POST
32367  * @cfg {String} url action url
32368  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32369  * @cfg {Boolean} multiple multiple upload default true
32370  * @cfg {Number} thumbSize default 300
32371  * @cfg {String} fieldLabel
32372  * @cfg {Number} labelWidth default 4
32373  * @cfg {String} labelAlign (left|top) default left
32374  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32375 * @cfg {Number} labellg set the width of label (1-12)
32376  * @cfg {Number} labelmd set the width of label (1-12)
32377  * @cfg {Number} labelsm set the width of label (1-12)
32378  * @cfg {Number} labelxs set the width of label (1-12)
32379  * 
32380  * @constructor
32381  * Create a new DocumentManager
32382  * @param {Object} config The config object
32383  */
32384
32385 Roo.bootstrap.DocumentManager = function(config){
32386     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32387     
32388     this.files = [];
32389     this.delegates = [];
32390     
32391     this.addEvents({
32392         /**
32393          * @event initial
32394          * Fire when initial the DocumentManager
32395          * @param {Roo.bootstrap.DocumentManager} this
32396          */
32397         "initial" : true,
32398         /**
32399          * @event inspect
32400          * inspect selected file
32401          * @param {Roo.bootstrap.DocumentManager} this
32402          * @param {File} file
32403          */
32404         "inspect" : true,
32405         /**
32406          * @event exception
32407          * Fire when xhr load exception
32408          * @param {Roo.bootstrap.DocumentManager} this
32409          * @param {XMLHttpRequest} xhr
32410          */
32411         "exception" : true,
32412         /**
32413          * @event afterupload
32414          * Fire when xhr load exception
32415          * @param {Roo.bootstrap.DocumentManager} this
32416          * @param {XMLHttpRequest} xhr
32417          */
32418         "afterupload" : true,
32419         /**
32420          * @event prepare
32421          * prepare the form data
32422          * @param {Roo.bootstrap.DocumentManager} this
32423          * @param {Object} formData
32424          */
32425         "prepare" : true,
32426         /**
32427          * @event remove
32428          * Fire when remove the file
32429          * @param {Roo.bootstrap.DocumentManager} this
32430          * @param {Object} file
32431          */
32432         "remove" : true,
32433         /**
32434          * @event refresh
32435          * Fire after refresh the file
32436          * @param {Roo.bootstrap.DocumentManager} this
32437          */
32438         "refresh" : true,
32439         /**
32440          * @event click
32441          * Fire after click the image
32442          * @param {Roo.bootstrap.DocumentManager} this
32443          * @param {Object} file
32444          */
32445         "click" : true,
32446         /**
32447          * @event edit
32448          * Fire when upload a image and editable set to true
32449          * @param {Roo.bootstrap.DocumentManager} this
32450          * @param {Object} file
32451          */
32452         "edit" : true,
32453         /**
32454          * @event beforeselectfile
32455          * Fire before select file
32456          * @param {Roo.bootstrap.DocumentManager} this
32457          */
32458         "beforeselectfile" : true,
32459         /**
32460          * @event process
32461          * Fire before process file
32462          * @param {Roo.bootstrap.DocumentManager} this
32463          * @param {Object} file
32464          */
32465         "process" : true,
32466         /**
32467          * @event previewrendered
32468          * Fire when preview rendered
32469          * @param {Roo.bootstrap.DocumentManager} this
32470          * @param {Object} file
32471          */
32472         "previewrendered" : true,
32473         /**
32474          */
32475         "previewResize" : true
32476         
32477     });
32478 };
32479
32480 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32481     
32482     boxes : 0,
32483     inputName : '',
32484     thumbSize : 300,
32485     multiple : true,
32486     files : false,
32487     method : 'POST',
32488     url : '',
32489     paramName : 'imageUpload',
32490     toolTipName : 'filename',
32491     fieldLabel : '',
32492     labelWidth : 4,
32493     labelAlign : 'left',
32494     editable : true,
32495     delegates : false,
32496     xhr : false, 
32497     
32498     labellg : 0,
32499     labelmd : 0,
32500     labelsm : 0,
32501     labelxs : 0,
32502     
32503     getAutoCreate : function()
32504     {   
32505         var managerWidget = {
32506             tag : 'div',
32507             cls : 'roo-document-manager',
32508             cn : [
32509                 {
32510                     tag : 'input',
32511                     cls : 'roo-document-manager-selector',
32512                     type : 'file'
32513                 },
32514                 {
32515                     tag : 'div',
32516                     cls : 'roo-document-manager-uploader',
32517                     cn : [
32518                         {
32519                             tag : 'div',
32520                             cls : 'roo-document-manager-upload-btn',
32521                             html : '<i class="fa fa-plus"></i>'
32522                         }
32523                     ]
32524                     
32525                 }
32526             ]
32527         };
32528         
32529         var content = [
32530             {
32531                 tag : 'div',
32532                 cls : 'column col-md-12',
32533                 cn : managerWidget
32534             }
32535         ];
32536         
32537         if(this.fieldLabel.length){
32538             
32539             content = [
32540                 {
32541                     tag : 'div',
32542                     cls : 'column col-md-12',
32543                     html : this.fieldLabel
32544                 },
32545                 {
32546                     tag : 'div',
32547                     cls : 'column col-md-12',
32548                     cn : managerWidget
32549                 }
32550             ];
32551
32552             if(this.labelAlign == 'left'){
32553                 content = [
32554                     {
32555                         tag : 'div',
32556                         cls : 'column',
32557                         html : this.fieldLabel
32558                     },
32559                     {
32560                         tag : 'div',
32561                         cls : 'column',
32562                         cn : managerWidget
32563                     }
32564                 ];
32565                 
32566                 if(this.labelWidth > 12){
32567                     content[0].style = "width: " + this.labelWidth + 'px';
32568                 }
32569
32570                 if(this.labelWidth < 13 && this.labelmd == 0){
32571                     this.labelmd = this.labelWidth;
32572                 }
32573
32574                 if(this.labellg > 0){
32575                     content[0].cls += ' col-lg-' + this.labellg;
32576                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32577                 }
32578
32579                 if(this.labelmd > 0){
32580                     content[0].cls += ' col-md-' + this.labelmd;
32581                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32582                 }
32583
32584                 if(this.labelsm > 0){
32585                     content[0].cls += ' col-sm-' + this.labelsm;
32586                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32587                 }
32588
32589                 if(this.labelxs > 0){
32590                     content[0].cls += ' col-xs-' + this.labelxs;
32591                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32592                 }
32593                 
32594             }
32595         }
32596         
32597         var cfg = {
32598             tag : 'div',
32599             cls : 'row clearfix',
32600             cn : content
32601         };
32602         
32603         return cfg;
32604         
32605     },
32606     
32607     initEvents : function()
32608     {
32609         this.managerEl = this.el.select('.roo-document-manager', true).first();
32610         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32611         
32612         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32613         this.selectorEl.hide();
32614         
32615         if(this.multiple){
32616             this.selectorEl.attr('multiple', 'multiple');
32617         }
32618         
32619         this.selectorEl.on('change', this.onFileSelected, this);
32620         
32621         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32622         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32623         
32624         this.uploader.on('click', this.onUploaderClick, this);
32625         
32626         this.renderProgressDialog();
32627         
32628         var _this = this;
32629         
32630         window.addEventListener("resize", function() { _this.refresh(); } );
32631         
32632         this.fireEvent('initial', this);
32633     },
32634     
32635     renderProgressDialog : function()
32636     {
32637         var _this = this;
32638         
32639         this.progressDialog = new Roo.bootstrap.Modal({
32640             cls : 'roo-document-manager-progress-dialog',
32641             allow_close : false,
32642             animate : false,
32643             title : '',
32644             buttons : [
32645                 {
32646                     name  :'cancel',
32647                     weight : 'danger',
32648                     html : 'Cancel'
32649                 }
32650             ], 
32651             listeners : { 
32652                 btnclick : function() {
32653                     _this.uploadCancel();
32654                     this.hide();
32655                 }
32656             }
32657         });
32658          
32659         this.progressDialog.render(Roo.get(document.body));
32660          
32661         this.progress = new Roo.bootstrap.Progress({
32662             cls : 'roo-document-manager-progress',
32663             active : true,
32664             striped : true
32665         });
32666         
32667         this.progress.render(this.progressDialog.getChildContainer());
32668         
32669         this.progressBar = new Roo.bootstrap.ProgressBar({
32670             cls : 'roo-document-manager-progress-bar',
32671             aria_valuenow : 0,
32672             aria_valuemin : 0,
32673             aria_valuemax : 12,
32674             panel : 'success'
32675         });
32676         
32677         this.progressBar.render(this.progress.getChildContainer());
32678     },
32679     
32680     onUploaderClick : function(e)
32681     {
32682         e.preventDefault();
32683      
32684         if(this.fireEvent('beforeselectfile', this) != false){
32685             this.selectorEl.dom.click();
32686         }
32687         
32688     },
32689     
32690     onFileSelected : function(e)
32691     {
32692         e.preventDefault();
32693         
32694         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32695             return;
32696         }
32697         
32698         Roo.each(this.selectorEl.dom.files, function(file){
32699             if(this.fireEvent('inspect', this, file) != false){
32700                 this.files.push(file);
32701             }
32702         }, this);
32703         
32704         this.queue();
32705         
32706     },
32707     
32708     queue : function()
32709     {
32710         this.selectorEl.dom.value = '';
32711         
32712         if(!this.files || !this.files.length){
32713             return;
32714         }
32715         
32716         if(this.boxes > 0 && this.files.length > this.boxes){
32717             this.files = this.files.slice(0, this.boxes);
32718         }
32719         
32720         this.uploader.show();
32721         
32722         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32723             this.uploader.hide();
32724         }
32725         
32726         var _this = this;
32727         
32728         var files = [];
32729         
32730         var docs = [];
32731         
32732         Roo.each(this.files, function(file){
32733             
32734             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32735                 var f = this.renderPreview(file);
32736                 files.push(f);
32737                 return;
32738             }
32739             
32740             if(file.type.indexOf('image') != -1){
32741                 this.delegates.push(
32742                     (function(){
32743                         _this.process(file);
32744                     }).createDelegate(this)
32745                 );
32746         
32747                 return;
32748             }
32749             
32750             docs.push(
32751                 (function(){
32752                     _this.process(file);
32753                 }).createDelegate(this)
32754             );
32755             
32756         }, this);
32757         
32758         this.files = files;
32759         
32760         this.delegates = this.delegates.concat(docs);
32761         
32762         if(!this.delegates.length){
32763             this.refresh();
32764             return;
32765         }
32766         
32767         this.progressBar.aria_valuemax = this.delegates.length;
32768         
32769         this.arrange();
32770         
32771         return;
32772     },
32773     
32774     arrange : function()
32775     {
32776         if(!this.delegates.length){
32777             this.progressDialog.hide();
32778             this.refresh();
32779             return;
32780         }
32781         
32782         var delegate = this.delegates.shift();
32783         
32784         this.progressDialog.show();
32785         
32786         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32787         
32788         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32789         
32790         delegate();
32791     },
32792     
32793     refresh : function()
32794     {
32795         this.uploader.show();
32796         
32797         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32798             this.uploader.hide();
32799         }
32800         
32801         Roo.isTouch ? this.closable(false) : this.closable(true);
32802         
32803         this.fireEvent('refresh', this);
32804     },
32805     
32806     onRemove : function(e, el, o)
32807     {
32808         e.preventDefault();
32809         
32810         this.fireEvent('remove', this, o);
32811         
32812     },
32813     
32814     remove : function(o)
32815     {
32816         var files = [];
32817         
32818         Roo.each(this.files, function(file){
32819             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32820                 files.push(file);
32821                 return;
32822             }
32823
32824             o.target.remove();
32825
32826         }, this);
32827         
32828         this.files = files;
32829         
32830         this.refresh();
32831     },
32832     
32833     clear : function()
32834     {
32835         Roo.each(this.files, function(file){
32836             if(!file.target){
32837                 return;
32838             }
32839             
32840             file.target.remove();
32841
32842         }, this);
32843         
32844         this.files = [];
32845         
32846         this.refresh();
32847     },
32848     
32849     onClick : function(e, el, o)
32850     {
32851         e.preventDefault();
32852         
32853         this.fireEvent('click', this, o);
32854         
32855     },
32856     
32857     closable : function(closable)
32858     {
32859         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32860             
32861             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32862             
32863             if(closable){
32864                 el.show();
32865                 return;
32866             }
32867             
32868             el.hide();
32869             
32870         }, this);
32871     },
32872     
32873     xhrOnLoad : function(xhr)
32874     {
32875         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32876             el.remove();
32877         }, this);
32878         
32879         if (xhr.readyState !== 4) {
32880             this.arrange();
32881             this.fireEvent('exception', this, xhr);
32882             return;
32883         }
32884
32885         var response = Roo.decode(xhr.responseText);
32886         
32887         if(!response.success){
32888             this.arrange();
32889             this.fireEvent('exception', this, xhr);
32890             return;
32891         }
32892         
32893         var file = this.renderPreview(response.data);
32894         
32895         this.files.push(file);
32896         
32897         this.arrange();
32898         
32899         this.fireEvent('afterupload', this, xhr);
32900         
32901     },
32902     
32903     xhrOnError : function(xhr)
32904     {
32905         Roo.log('xhr on error');
32906         
32907         var response = Roo.decode(xhr.responseText);
32908           
32909         Roo.log(response);
32910         
32911         this.arrange();
32912     },
32913     
32914     process : function(file)
32915     {
32916         if(this.fireEvent('process', this, file) !== false){
32917             if(this.editable && file.type.indexOf('image') != -1){
32918                 this.fireEvent('edit', this, file);
32919                 return;
32920             }
32921
32922             this.uploadStart(file, false);
32923
32924             return;
32925         }
32926         
32927     },
32928     
32929     uploadStart : function(file, crop)
32930     {
32931         this.xhr = new XMLHttpRequest();
32932         
32933         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32934             this.arrange();
32935             return;
32936         }
32937         
32938         file.xhr = this.xhr;
32939             
32940         this.managerEl.createChild({
32941             tag : 'div',
32942             cls : 'roo-document-manager-loading',
32943             cn : [
32944                 {
32945                     tag : 'div',
32946                     tooltip : file.name,
32947                     cls : 'roo-document-manager-thumb',
32948                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32949                 }
32950             ]
32951
32952         });
32953
32954         this.xhr.open(this.method, this.url, true);
32955         
32956         var headers = {
32957             "Accept": "application/json",
32958             "Cache-Control": "no-cache",
32959             "X-Requested-With": "XMLHttpRequest"
32960         };
32961         
32962         for (var headerName in headers) {
32963             var headerValue = headers[headerName];
32964             if (headerValue) {
32965                 this.xhr.setRequestHeader(headerName, headerValue);
32966             }
32967         }
32968         
32969         var _this = this;
32970         
32971         this.xhr.onload = function()
32972         {
32973             _this.xhrOnLoad(_this.xhr);
32974         }
32975         
32976         this.xhr.onerror = function()
32977         {
32978             _this.xhrOnError(_this.xhr);
32979         }
32980         
32981         var formData = new FormData();
32982
32983         formData.append('returnHTML', 'NO');
32984         
32985         if(crop){
32986             formData.append('crop', crop);
32987         }
32988         
32989         formData.append(this.paramName, file, file.name);
32990         
32991         var options = {
32992             file : file, 
32993             manually : false
32994         };
32995         
32996         if(this.fireEvent('prepare', this, formData, options) != false){
32997             
32998             if(options.manually){
32999                 return;
33000             }
33001             
33002             this.xhr.send(formData);
33003             return;
33004         };
33005         
33006         this.uploadCancel();
33007     },
33008     
33009     uploadCancel : function()
33010     {
33011         if (this.xhr) {
33012             this.xhr.abort();
33013         }
33014         
33015         this.delegates = [];
33016         
33017         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33018             el.remove();
33019         }, this);
33020         
33021         this.arrange();
33022     },
33023     
33024     renderPreview : function(file)
33025     {
33026         if(typeof(file.target) != 'undefined' && file.target){
33027             return file;
33028         }
33029         
33030         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33031         
33032         var previewEl = this.managerEl.createChild({
33033             tag : 'div',
33034             cls : 'roo-document-manager-preview',
33035             cn : [
33036                 {
33037                     tag : 'div',
33038                     tooltip : file[this.toolTipName],
33039                     cls : 'roo-document-manager-thumb',
33040                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33041                 },
33042                 {
33043                     tag : 'button',
33044                     cls : 'close',
33045                     html : '<i class="fa fa-times-circle"></i>'
33046                 }
33047             ]
33048         });
33049
33050         var close = previewEl.select('button.close', true).first();
33051
33052         close.on('click', this.onRemove, this, file);
33053
33054         file.target = previewEl;
33055
33056         var image = previewEl.select('img', true).first();
33057         
33058         var _this = this;
33059         
33060         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33061         
33062         image.on('click', this.onClick, this, file);
33063         
33064         this.fireEvent('previewrendered', this, file);
33065         
33066         return file;
33067         
33068     },
33069     
33070     onPreviewLoad : function(file, image)
33071     {
33072         if(typeof(file.target) == 'undefined' || !file.target){
33073             return;
33074         }
33075         
33076         var width = image.dom.naturalWidth || image.dom.width;
33077         var height = image.dom.naturalHeight || image.dom.height;
33078         
33079         if(!this.previewResize) {
33080             return;
33081         }
33082         
33083         if(width > height){
33084             file.target.addClass('wide');
33085             return;
33086         }
33087         
33088         file.target.addClass('tall');
33089         return;
33090         
33091     },
33092     
33093     uploadFromSource : function(file, crop)
33094     {
33095         this.xhr = new XMLHttpRequest();
33096         
33097         this.managerEl.createChild({
33098             tag : 'div',
33099             cls : 'roo-document-manager-loading',
33100             cn : [
33101                 {
33102                     tag : 'div',
33103                     tooltip : file.name,
33104                     cls : 'roo-document-manager-thumb',
33105                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33106                 }
33107             ]
33108
33109         });
33110
33111         this.xhr.open(this.method, this.url, true);
33112         
33113         var headers = {
33114             "Accept": "application/json",
33115             "Cache-Control": "no-cache",
33116             "X-Requested-With": "XMLHttpRequest"
33117         };
33118         
33119         for (var headerName in headers) {
33120             var headerValue = headers[headerName];
33121             if (headerValue) {
33122                 this.xhr.setRequestHeader(headerName, headerValue);
33123             }
33124         }
33125         
33126         var _this = this;
33127         
33128         this.xhr.onload = function()
33129         {
33130             _this.xhrOnLoad(_this.xhr);
33131         }
33132         
33133         this.xhr.onerror = function()
33134         {
33135             _this.xhrOnError(_this.xhr);
33136         }
33137         
33138         var formData = new FormData();
33139
33140         formData.append('returnHTML', 'NO');
33141         
33142         formData.append('crop', crop);
33143         
33144         if(typeof(file.filename) != 'undefined'){
33145             formData.append('filename', file.filename);
33146         }
33147         
33148         if(typeof(file.mimetype) != 'undefined'){
33149             formData.append('mimetype', file.mimetype);
33150         }
33151         
33152         Roo.log(formData);
33153         
33154         if(this.fireEvent('prepare', this, formData) != false){
33155             this.xhr.send(formData);
33156         };
33157     }
33158 });
33159
33160 /*
33161 * Licence: LGPL
33162 */
33163
33164 /**
33165  * @class Roo.bootstrap.DocumentViewer
33166  * @extends Roo.bootstrap.Component
33167  * Bootstrap DocumentViewer class
33168  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33169  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33170  * 
33171  * @constructor
33172  * Create a new DocumentViewer
33173  * @param {Object} config The config object
33174  */
33175
33176 Roo.bootstrap.DocumentViewer = function(config){
33177     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33178     
33179     this.addEvents({
33180         /**
33181          * @event initial
33182          * Fire after initEvent
33183          * @param {Roo.bootstrap.DocumentViewer} this
33184          */
33185         "initial" : true,
33186         /**
33187          * @event click
33188          * Fire after click
33189          * @param {Roo.bootstrap.DocumentViewer} this
33190          */
33191         "click" : true,
33192         /**
33193          * @event download
33194          * Fire after download button
33195          * @param {Roo.bootstrap.DocumentViewer} this
33196          */
33197         "download" : true,
33198         /**
33199          * @event trash
33200          * Fire after trash button
33201          * @param {Roo.bootstrap.DocumentViewer} this
33202          */
33203         "trash" : true
33204         
33205     });
33206 };
33207
33208 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33209     
33210     showDownload : true,
33211     
33212     showTrash : true,
33213     
33214     getAutoCreate : function()
33215     {
33216         var cfg = {
33217             tag : 'div',
33218             cls : 'roo-document-viewer',
33219             cn : [
33220                 {
33221                     tag : 'div',
33222                     cls : 'roo-document-viewer-body',
33223                     cn : [
33224                         {
33225                             tag : 'div',
33226                             cls : 'roo-document-viewer-thumb',
33227                             cn : [
33228                                 {
33229                                     tag : 'img',
33230                                     cls : 'roo-document-viewer-image'
33231                                 }
33232                             ]
33233                         }
33234                     ]
33235                 },
33236                 {
33237                     tag : 'div',
33238                     cls : 'roo-document-viewer-footer',
33239                     cn : {
33240                         tag : 'div',
33241                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33242                         cn : [
33243                             {
33244                                 tag : 'div',
33245                                 cls : 'btn-group roo-document-viewer-download',
33246                                 cn : [
33247                                     {
33248                                         tag : 'button',
33249                                         cls : 'btn btn-default',
33250                                         html : '<i class="fa fa-download"></i>'
33251                                     }
33252                                 ]
33253                             },
33254                             {
33255                                 tag : 'div',
33256                                 cls : 'btn-group roo-document-viewer-trash',
33257                                 cn : [
33258                                     {
33259                                         tag : 'button',
33260                                         cls : 'btn btn-default',
33261                                         html : '<i class="fa fa-trash"></i>'
33262                                     }
33263                                 ]
33264                             }
33265                         ]
33266                     }
33267                 }
33268             ]
33269         };
33270         
33271         return cfg;
33272     },
33273     
33274     initEvents : function()
33275     {
33276         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33277         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33278         
33279         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33280         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33281         
33282         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33283         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33284         
33285         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33286         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33287         
33288         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33289         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33290         
33291         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33292         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33293         
33294         this.bodyEl.on('click', this.onClick, this);
33295         this.downloadBtn.on('click', this.onDownload, this);
33296         this.trashBtn.on('click', this.onTrash, this);
33297         
33298         this.downloadBtn.hide();
33299         this.trashBtn.hide();
33300         
33301         if(this.showDownload){
33302             this.downloadBtn.show();
33303         }
33304         
33305         if(this.showTrash){
33306             this.trashBtn.show();
33307         }
33308         
33309         if(!this.showDownload && !this.showTrash) {
33310             this.footerEl.hide();
33311         }
33312         
33313     },
33314     
33315     initial : function()
33316     {
33317         this.fireEvent('initial', this);
33318         
33319     },
33320     
33321     onClick : function(e)
33322     {
33323         e.preventDefault();
33324         
33325         this.fireEvent('click', this);
33326     },
33327     
33328     onDownload : function(e)
33329     {
33330         e.preventDefault();
33331         
33332         this.fireEvent('download', this);
33333     },
33334     
33335     onTrash : function(e)
33336     {
33337         e.preventDefault();
33338         
33339         this.fireEvent('trash', this);
33340     }
33341     
33342 });
33343 /*
33344  * - LGPL
33345  *
33346  * FieldLabel
33347  * 
33348  */
33349
33350 /**
33351  * @class Roo.bootstrap.form.FieldLabel
33352  * @extends Roo.bootstrap.Component
33353  * Bootstrap FieldLabel class
33354  * @cfg {String} html contents of the element
33355  * @cfg {String} tag tag of the element default label
33356  * @cfg {String} cls class of the element
33357  * @cfg {String} target label target 
33358  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33359  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33360  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33361  * @cfg {String} iconTooltip default "This field is required"
33362  * @cfg {String} indicatorpos (left|right) default left
33363  * 
33364  * @constructor
33365  * Create a new FieldLabel
33366  * @param {Object} config The config object
33367  */
33368
33369 Roo.bootstrap.form.FieldLabel = function(config){
33370     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33371     
33372     this.addEvents({
33373             /**
33374              * @event invalid
33375              * Fires after the field has been marked as invalid.
33376              * @param {Roo.form.FieldLabel} this
33377              * @param {String} msg The validation message
33378              */
33379             invalid : true,
33380             /**
33381              * @event valid
33382              * Fires after the field has been validated with no errors.
33383              * @param {Roo.form.FieldLabel} this
33384              */
33385             valid : true
33386         });
33387 };
33388
33389 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
33390     
33391     tag: 'label',
33392     cls: '',
33393     html: '',
33394     target: '',
33395     allowBlank : true,
33396     invalidClass : 'has-warning',
33397     validClass : 'has-success',
33398     iconTooltip : 'This field is required',
33399     indicatorpos : 'left',
33400     
33401     getAutoCreate : function(){
33402         
33403         var cls = "";
33404         if (!this.allowBlank) {
33405             cls  = "visible";
33406         }
33407         
33408         var cfg = {
33409             tag : this.tag,
33410             cls : 'roo-bootstrap-field-label ' + this.cls,
33411             for : this.target,
33412             cn : [
33413                 {
33414                     tag : 'i',
33415                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33416                     tooltip : this.iconTooltip
33417                 },
33418                 {
33419                     tag : 'span',
33420                     html : this.html
33421                 }
33422             ] 
33423         };
33424         
33425         if(this.indicatorpos == 'right'){
33426             var cfg = {
33427                 tag : this.tag,
33428                 cls : 'roo-bootstrap-field-label ' + this.cls,
33429                 for : this.target,
33430                 cn : [
33431                     {
33432                         tag : 'span',
33433                         html : this.html
33434                     },
33435                     {
33436                         tag : 'i',
33437                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33438                         tooltip : this.iconTooltip
33439                     }
33440                 ] 
33441             };
33442         }
33443         
33444         return cfg;
33445     },
33446     
33447     initEvents: function() 
33448     {
33449         Roo.bootstrap.Element.superclass.initEvents.call(this);
33450         
33451         this.indicator = this.indicatorEl();
33452         
33453         if(this.indicator){
33454             this.indicator.removeClass('visible');
33455             this.indicator.addClass('invisible');
33456         }
33457         
33458         Roo.bootstrap.form.FieldLabel.register(this);
33459     },
33460     
33461     indicatorEl : function()
33462     {
33463         var indicator = this.el.select('i.roo-required-indicator',true).first();
33464         
33465         if(!indicator){
33466             return false;
33467         }
33468         
33469         return indicator;
33470         
33471     },
33472     
33473     /**
33474      * Mark this field as valid
33475      */
33476     markValid : function()
33477     {
33478         if(this.indicator){
33479             this.indicator.removeClass('visible');
33480             this.indicator.addClass('invisible');
33481         }
33482         if (Roo.bootstrap.version == 3) {
33483             this.el.removeClass(this.invalidClass);
33484             this.el.addClass(this.validClass);
33485         } else {
33486             this.el.removeClass('is-invalid');
33487             this.el.addClass('is-valid');
33488         }
33489         
33490         
33491         this.fireEvent('valid', this);
33492     },
33493     
33494     /**
33495      * Mark this field as invalid
33496      * @param {String} msg The validation message
33497      */
33498     markInvalid : function(msg)
33499     {
33500         if(this.indicator){
33501             this.indicator.removeClass('invisible');
33502             this.indicator.addClass('visible');
33503         }
33504           if (Roo.bootstrap.version == 3) {
33505             this.el.removeClass(this.validClass);
33506             this.el.addClass(this.invalidClass);
33507         } else {
33508             this.el.removeClass('is-valid');
33509             this.el.addClass('is-invalid');
33510         }
33511         
33512         
33513         this.fireEvent('invalid', this, msg);
33514     }
33515     
33516    
33517 });
33518
33519 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33520     
33521     groups: {},
33522     
33523      /**
33524     * register a FieldLabel Group
33525     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33526     */
33527     register : function(label)
33528     {
33529         if(this.groups.hasOwnProperty(label.target)){
33530             return;
33531         }
33532      
33533         this.groups[label.target] = label;
33534         
33535     },
33536     /**
33537     * fetch a FieldLabel Group based on the target
33538     * @param {string} target
33539     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33540     */
33541     get: function(target) {
33542         if (typeof(this.groups[target]) == 'undefined') {
33543             return false;
33544         }
33545         
33546         return this.groups[target] ;
33547     }
33548 });
33549
33550  
33551
33552  /*
33553  * - LGPL
33554  *
33555  * page DateSplitField.
33556  * 
33557  */
33558
33559
33560 /**
33561  * @class Roo.bootstrap.form.DateSplitField
33562  * @extends Roo.bootstrap.Component
33563  * Bootstrap DateSplitField class
33564  * @cfg {string} fieldLabel - the label associated
33565  * @cfg {Number} labelWidth set the width of label (0-12)
33566  * @cfg {String} labelAlign (top|left)
33567  * @cfg {Boolean} dayAllowBlank (true|false) default false
33568  * @cfg {Boolean} monthAllowBlank (true|false) default false
33569  * @cfg {Boolean} yearAllowBlank (true|false) default false
33570  * @cfg {string} dayPlaceholder 
33571  * @cfg {string} monthPlaceholder
33572  * @cfg {string} yearPlaceholder
33573  * @cfg {string} dayFormat default 'd'
33574  * @cfg {string} monthFormat default 'm'
33575  * @cfg {string} yearFormat default 'Y'
33576  * @cfg {Number} labellg set the width of label (1-12)
33577  * @cfg {Number} labelmd set the width of label (1-12)
33578  * @cfg {Number} labelsm set the width of label (1-12)
33579  * @cfg {Number} labelxs set the width of label (1-12)
33580
33581  *     
33582  * @constructor
33583  * Create a new DateSplitField
33584  * @param {Object} config The config object
33585  */
33586
33587 Roo.bootstrap.form.DateSplitField = function(config){
33588     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33589     
33590     this.addEvents({
33591         // raw events
33592          /**
33593          * @event years
33594          * getting the data of years
33595          * @param {Roo.bootstrap.form.DateSplitField} this
33596          * @param {Object} years
33597          */
33598         "years" : true,
33599         /**
33600          * @event days
33601          * getting the data of days
33602          * @param {Roo.bootstrap.form.DateSplitField} this
33603          * @param {Object} days
33604          */
33605         "days" : true,
33606         /**
33607          * @event invalid
33608          * Fires after the field has been marked as invalid.
33609          * @param {Roo.form.Field} this
33610          * @param {String} msg The validation message
33611          */
33612         invalid : true,
33613        /**
33614          * @event valid
33615          * Fires after the field has been validated with no errors.
33616          * @param {Roo.form.Field} this
33617          */
33618         valid : true
33619     });
33620 };
33621
33622 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
33623     
33624     fieldLabel : '',
33625     labelAlign : 'top',
33626     labelWidth : 3,
33627     dayAllowBlank : false,
33628     monthAllowBlank : false,
33629     yearAllowBlank : false,
33630     dayPlaceholder : '',
33631     monthPlaceholder : '',
33632     yearPlaceholder : '',
33633     dayFormat : 'd',
33634     monthFormat : 'm',
33635     yearFormat : 'Y',
33636     isFormField : true,
33637     labellg : 0,
33638     labelmd : 0,
33639     labelsm : 0,
33640     labelxs : 0,
33641     
33642     getAutoCreate : function()
33643     {
33644         var cfg = {
33645             tag : 'div',
33646             cls : 'row roo-date-split-field-group',
33647             cn : [
33648                 {
33649                     tag : 'input',
33650                     type : 'hidden',
33651                     cls : 'form-hidden-field roo-date-split-field-group-value',
33652                     name : this.name
33653                 }
33654             ]
33655         };
33656         
33657         var labelCls = 'col-md-12';
33658         var contentCls = 'col-md-4';
33659         
33660         if(this.fieldLabel){
33661             
33662             var label = {
33663                 tag : 'div',
33664                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33665                 cn : [
33666                     {
33667                         tag : 'label',
33668                         html : this.fieldLabel
33669                     }
33670                 ]
33671             };
33672             
33673             if(this.labelAlign == 'left'){
33674             
33675                 if(this.labelWidth > 12){
33676                     label.style = "width: " + this.labelWidth + 'px';
33677                 }
33678
33679                 if(this.labelWidth < 13 && this.labelmd == 0){
33680                     this.labelmd = this.labelWidth;
33681                 }
33682
33683                 if(this.labellg > 0){
33684                     labelCls = ' col-lg-' + this.labellg;
33685                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33686                 }
33687
33688                 if(this.labelmd > 0){
33689                     labelCls = ' col-md-' + this.labelmd;
33690                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33691                 }
33692
33693                 if(this.labelsm > 0){
33694                     labelCls = ' col-sm-' + this.labelsm;
33695                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33696                 }
33697
33698                 if(this.labelxs > 0){
33699                     labelCls = ' col-xs-' + this.labelxs;
33700                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33701                 }
33702             }
33703             
33704             label.cls += ' ' + labelCls;
33705             
33706             cfg.cn.push(label);
33707         }
33708         
33709         Roo.each(['day', 'month', 'year'], function(t){
33710             cfg.cn.push({
33711                 tag : 'div',
33712                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33713             });
33714         }, this);
33715         
33716         return cfg;
33717     },
33718     
33719     inputEl: function ()
33720     {
33721         return this.el.select('.roo-date-split-field-group-value', true).first();
33722     },
33723     
33724     onRender : function(ct, position) 
33725     {
33726         var _this = this;
33727         
33728         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33729         
33730         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33731         
33732         this.dayField = new Roo.bootstrap.form.ComboBox({
33733             allowBlank : this.dayAllowBlank,
33734             alwaysQuery : true,
33735             displayField : 'value',
33736             editable : false,
33737             fieldLabel : '',
33738             forceSelection : true,
33739             mode : 'local',
33740             placeholder : this.dayPlaceholder,
33741             selectOnFocus : true,
33742             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33743             triggerAction : 'all',
33744             typeAhead : true,
33745             valueField : 'value',
33746             store : new Roo.data.SimpleStore({
33747                 data : (function() {    
33748                     var days = [];
33749                     _this.fireEvent('days', _this, days);
33750                     return days;
33751                 })(),
33752                 fields : [ 'value' ]
33753             }),
33754             listeners : {
33755                 select : function (_self, record, index)
33756                 {
33757                     _this.setValue(_this.getValue());
33758                 }
33759             }
33760         });
33761
33762         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33763         
33764         this.monthField = new Roo.bootstrap.form.MonthField({
33765             after : '<i class=\"fa fa-calendar\"></i>',
33766             allowBlank : this.monthAllowBlank,
33767             placeholder : this.monthPlaceholder,
33768             readOnly : true,
33769             listeners : {
33770                 render : function (_self)
33771                 {
33772                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33773                         e.preventDefault();
33774                         _self.focus();
33775                     });
33776                 },
33777                 select : function (_self, oldvalue, newvalue)
33778                 {
33779                     _this.setValue(_this.getValue());
33780                 }
33781             }
33782         });
33783         
33784         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33785         
33786         this.yearField = new Roo.bootstrap.form.ComboBox({
33787             allowBlank : this.yearAllowBlank,
33788             alwaysQuery : true,
33789             displayField : 'value',
33790             editable : false,
33791             fieldLabel : '',
33792             forceSelection : true,
33793             mode : 'local',
33794             placeholder : this.yearPlaceholder,
33795             selectOnFocus : true,
33796             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33797             triggerAction : 'all',
33798             typeAhead : true,
33799             valueField : 'value',
33800             store : new Roo.data.SimpleStore({
33801                 data : (function() {
33802                     var years = [];
33803                     _this.fireEvent('years', _this, years);
33804                     return years;
33805                 })(),
33806                 fields : [ 'value' ]
33807             }),
33808             listeners : {
33809                 select : function (_self, record, index)
33810                 {
33811                     _this.setValue(_this.getValue());
33812                 }
33813             }
33814         });
33815
33816         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33817     },
33818     
33819     setValue : function(v, format)
33820     {
33821         this.inputEl.dom.value = v;
33822         
33823         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33824         
33825         var d = Date.parseDate(v, f);
33826         
33827         if(!d){
33828             this.validate();
33829             return;
33830         }
33831         
33832         this.setDay(d.format(this.dayFormat));
33833         this.setMonth(d.format(this.monthFormat));
33834         this.setYear(d.format(this.yearFormat));
33835         
33836         this.validate();
33837         
33838         return;
33839     },
33840     
33841     setDay : function(v)
33842     {
33843         this.dayField.setValue(v);
33844         this.inputEl.dom.value = this.getValue();
33845         this.validate();
33846         return;
33847     },
33848     
33849     setMonth : function(v)
33850     {
33851         this.monthField.setValue(v, true);
33852         this.inputEl.dom.value = this.getValue();
33853         this.validate();
33854         return;
33855     },
33856     
33857     setYear : function(v)
33858     {
33859         this.yearField.setValue(v);
33860         this.inputEl.dom.value = this.getValue();
33861         this.validate();
33862         return;
33863     },
33864     
33865     getDay : function()
33866     {
33867         return this.dayField.getValue();
33868     },
33869     
33870     getMonth : function()
33871     {
33872         return this.monthField.getValue();
33873     },
33874     
33875     getYear : function()
33876     {
33877         return this.yearField.getValue();
33878     },
33879     
33880     getValue : function()
33881     {
33882         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33883         
33884         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33885         
33886         return date;
33887     },
33888     
33889     reset : function()
33890     {
33891         this.setDay('');
33892         this.setMonth('');
33893         this.setYear('');
33894         this.inputEl.dom.value = '';
33895         this.validate();
33896         return;
33897     },
33898     
33899     validate : function()
33900     {
33901         var d = this.dayField.validate();
33902         var m = this.monthField.validate();
33903         var y = this.yearField.validate();
33904         
33905         var valid = true;
33906         
33907         if(
33908                 (!this.dayAllowBlank && !d) ||
33909                 (!this.monthAllowBlank && !m) ||
33910                 (!this.yearAllowBlank && !y)
33911         ){
33912             valid = false;
33913         }
33914         
33915         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33916             return valid;
33917         }
33918         
33919         if(valid){
33920             this.markValid();
33921             return valid;
33922         }
33923         
33924         this.markInvalid();
33925         
33926         return valid;
33927     },
33928     
33929     markValid : function()
33930     {
33931         
33932         var label = this.el.select('label', true).first();
33933         var icon = this.el.select('i.fa-star', true).first();
33934
33935         if(label && icon){
33936             icon.remove();
33937         }
33938         
33939         this.fireEvent('valid', this);
33940     },
33941     
33942      /**
33943      * Mark this field as invalid
33944      * @param {String} msg The validation message
33945      */
33946     markInvalid : function(msg)
33947     {
33948         
33949         var label = this.el.select('label', true).first();
33950         var icon = this.el.select('i.fa-star', true).first();
33951
33952         if(label && !icon){
33953             this.el.select('.roo-date-split-field-label', true).createChild({
33954                 tag : 'i',
33955                 cls : 'text-danger fa fa-lg fa-star',
33956                 tooltip : 'This field is required',
33957                 style : 'margin-right:5px;'
33958             }, label, true);
33959         }
33960         
33961         this.fireEvent('invalid', this, msg);
33962     },
33963     
33964     clearInvalid : function()
33965     {
33966         var label = this.el.select('label', true).first();
33967         var icon = this.el.select('i.fa-star', true).first();
33968
33969         if(label && icon){
33970             icon.remove();
33971         }
33972         
33973         this.fireEvent('valid', this);
33974     },
33975     
33976     getName: function()
33977     {
33978         return this.name;
33979     }
33980     
33981 });
33982
33983  
33984
33985 /**
33986  * @class Roo.bootstrap.LayoutMasonry
33987  * @extends Roo.bootstrap.Component
33988  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
33989  * Bootstrap Layout Masonry class
33990  *
33991  * This is based on 
33992  * http://masonry.desandro.com
33993  *
33994  * The idea is to render all the bricks based on vertical width...
33995  *
33996  * The original code extends 'outlayer' - we might need to use that....
33997
33998  * @constructor
33999  * Create a new Element
34000  * @param {Object} config The config object
34001  */
34002
34003 Roo.bootstrap.LayoutMasonry = function(config){
34004     
34005     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34006     
34007     this.bricks = [];
34008     
34009     Roo.bootstrap.LayoutMasonry.register(this);
34010     
34011     this.addEvents({
34012         // raw events
34013         /**
34014          * @event layout
34015          * Fire after layout the items
34016          * @param {Roo.bootstrap.LayoutMasonry} this
34017          * @param {Roo.EventObject} e
34018          */
34019         "layout" : true
34020     });
34021     
34022 };
34023
34024 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34025     
34026     /**
34027      * @cfg {Boolean} isLayoutInstant = no animation?
34028      */   
34029     isLayoutInstant : false, // needed?
34030    
34031     /**
34032      * @cfg {Number} boxWidth  width of the columns
34033      */   
34034     boxWidth : 450,
34035     
34036       /**
34037      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34038      */   
34039     boxHeight : 0,
34040     
34041     /**
34042      * @cfg {Number} padWidth padding below box..
34043      */   
34044     padWidth : 10, 
34045     
34046     /**
34047      * @cfg {Number} gutter gutter width..
34048      */   
34049     gutter : 10,
34050     
34051      /**
34052      * @cfg {Number} maxCols maximum number of columns
34053      */   
34054     
34055     maxCols: 0,
34056     
34057     /**
34058      * @cfg {Boolean} isAutoInitial defalut true
34059      */   
34060     isAutoInitial : true, 
34061     
34062     containerWidth: 0,
34063     
34064     /**
34065      * @cfg {Boolean} isHorizontal defalut false
34066      */   
34067     isHorizontal : false, 
34068
34069     currentSize : null,
34070     
34071     tag: 'div',
34072     
34073     cls: '',
34074     
34075     bricks: null, //CompositeElement
34076     
34077     cols : 1,
34078     
34079     _isLayoutInited : false,
34080     
34081 //    isAlternative : false, // only use for vertical layout...
34082     
34083     /**
34084      * @cfg {Number} alternativePadWidth padding below box..
34085      */   
34086     alternativePadWidth : 50,
34087     
34088     selectedBrick : [],
34089     
34090     getAutoCreate : function(){
34091         
34092         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34093         
34094         var cfg = {
34095             tag: this.tag,
34096             cls: 'blog-masonary-wrapper ' + this.cls,
34097             cn : {
34098                 cls : 'mas-boxes masonary'
34099             }
34100         };
34101         
34102         return cfg;
34103     },
34104     
34105     getChildContainer: function( )
34106     {
34107         if (this.boxesEl) {
34108             return this.boxesEl;
34109         }
34110         
34111         this.boxesEl = this.el.select('.mas-boxes').first();
34112         
34113         return this.boxesEl;
34114     },
34115     
34116     
34117     initEvents : function()
34118     {
34119         var _this = this;
34120         
34121         if(this.isAutoInitial){
34122             Roo.log('hook children rendered');
34123             this.on('childrenrendered', function() {
34124                 Roo.log('children rendered');
34125                 _this.initial();
34126             } ,this);
34127         }
34128     },
34129     
34130     initial : function()
34131     {
34132         this.selectedBrick = [];
34133         
34134         this.currentSize = this.el.getBox(true);
34135         
34136         Roo.EventManager.onWindowResize(this.resize, this); 
34137
34138         if(!this.isAutoInitial){
34139             this.layout();
34140             return;
34141         }
34142         
34143         this.layout();
34144         
34145         return;
34146         //this.layout.defer(500,this);
34147         
34148     },
34149     
34150     resize : function()
34151     {
34152         var cs = this.el.getBox(true);
34153         
34154         if (
34155                 this.currentSize.width == cs.width && 
34156                 this.currentSize.x == cs.x && 
34157                 this.currentSize.height == cs.height && 
34158                 this.currentSize.y == cs.y 
34159         ) {
34160             Roo.log("no change in with or X or Y");
34161             return;
34162         }
34163         
34164         this.currentSize = cs;
34165         
34166         this.layout();
34167         
34168     },
34169     
34170     layout : function()
34171     {   
34172         this._resetLayout();
34173         
34174         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34175         
34176         this.layoutItems( isInstant );
34177       
34178         this._isLayoutInited = true;
34179         
34180         this.fireEvent('layout', this);
34181         
34182     },
34183     
34184     _resetLayout : function()
34185     {
34186         if(this.isHorizontal){
34187             this.horizontalMeasureColumns();
34188             return;
34189         }
34190         
34191         this.verticalMeasureColumns();
34192         
34193     },
34194     
34195     verticalMeasureColumns : function()
34196     {
34197         this.getContainerWidth();
34198         
34199 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34200 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34201 //            return;
34202 //        }
34203         
34204         var boxWidth = this.boxWidth + this.padWidth;
34205         
34206         if(this.containerWidth < this.boxWidth){
34207             boxWidth = this.containerWidth
34208         }
34209         
34210         var containerWidth = this.containerWidth;
34211         
34212         var cols = Math.floor(containerWidth / boxWidth);
34213         
34214         this.cols = Math.max( cols, 1 );
34215         
34216         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34217         
34218         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34219         
34220         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34221         
34222         this.colWidth = boxWidth + avail - this.padWidth;
34223         
34224         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34225         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34226     },
34227     
34228     horizontalMeasureColumns : function()
34229     {
34230         this.getContainerWidth();
34231         
34232         var boxWidth = this.boxWidth;
34233         
34234         if(this.containerWidth < boxWidth){
34235             boxWidth = this.containerWidth;
34236         }
34237         
34238         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34239         
34240         this.el.setHeight(boxWidth);
34241         
34242     },
34243     
34244     getContainerWidth : function()
34245     {
34246         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34247     },
34248     
34249     layoutItems : function( isInstant )
34250     {
34251         Roo.log(this.bricks);
34252         
34253         var items = Roo.apply([], this.bricks);
34254         
34255         if(this.isHorizontal){
34256             this._horizontalLayoutItems( items , isInstant );
34257             return;
34258         }
34259         
34260 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34261 //            this._verticalAlternativeLayoutItems( items , isInstant );
34262 //            return;
34263 //        }
34264         
34265         this._verticalLayoutItems( items , isInstant );
34266         
34267     },
34268     
34269     _verticalLayoutItems : function ( items , isInstant)
34270     {
34271         if ( !items || !items.length ) {
34272             return;
34273         }
34274         
34275         var standard = [
34276             ['xs', 'xs', 'xs', 'tall'],
34277             ['xs', 'xs', 'tall'],
34278             ['xs', 'xs', 'sm'],
34279             ['xs', 'xs', 'xs'],
34280             ['xs', 'tall'],
34281             ['xs', 'sm'],
34282             ['xs', 'xs'],
34283             ['xs'],
34284             
34285             ['sm', 'xs', 'xs'],
34286             ['sm', 'xs'],
34287             ['sm'],
34288             
34289             ['tall', 'xs', 'xs', 'xs'],
34290             ['tall', 'xs', 'xs'],
34291             ['tall', 'xs'],
34292             ['tall']
34293             
34294         ];
34295         
34296         var queue = [];
34297         
34298         var boxes = [];
34299         
34300         var box = [];
34301         
34302         Roo.each(items, function(item, k){
34303             
34304             switch (item.size) {
34305                 // these layouts take up a full box,
34306                 case 'md' :
34307                 case 'md-left' :
34308                 case 'md-right' :
34309                 case 'wide' :
34310                     
34311                     if(box.length){
34312                         boxes.push(box);
34313                         box = [];
34314                     }
34315                     
34316                     boxes.push([item]);
34317                     
34318                     break;
34319                     
34320                 case 'xs' :
34321                 case 'sm' :
34322                 case 'tall' :
34323                     
34324                     box.push(item);
34325                     
34326                     break;
34327                 default :
34328                     break;
34329                     
34330             }
34331             
34332         }, this);
34333         
34334         if(box.length){
34335             boxes.push(box);
34336             box = [];
34337         }
34338         
34339         var filterPattern = function(box, length)
34340         {
34341             if(!box.length){
34342                 return;
34343             }
34344             
34345             var match = false;
34346             
34347             var pattern = box.slice(0, length);
34348             
34349             var format = [];
34350             
34351             Roo.each(pattern, function(i){
34352                 format.push(i.size);
34353             }, this);
34354             
34355             Roo.each(standard, function(s){
34356                 
34357                 if(String(s) != String(format)){
34358                     return;
34359                 }
34360                 
34361                 match = true;
34362                 return false;
34363                 
34364             }, this);
34365             
34366             if(!match && length == 1){
34367                 return;
34368             }
34369             
34370             if(!match){
34371                 filterPattern(box, length - 1);
34372                 return;
34373             }
34374                 
34375             queue.push(pattern);
34376
34377             box = box.slice(length, box.length);
34378
34379             filterPattern(box, 4);
34380
34381             return;
34382             
34383         }
34384         
34385         Roo.each(boxes, function(box, k){
34386             
34387             if(!box.length){
34388                 return;
34389             }
34390             
34391             if(box.length == 1){
34392                 queue.push(box);
34393                 return;
34394             }
34395             
34396             filterPattern(box, 4);
34397             
34398         }, this);
34399         
34400         this._processVerticalLayoutQueue( queue, isInstant );
34401         
34402     },
34403     
34404 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34405 //    {
34406 //        if ( !items || !items.length ) {
34407 //            return;
34408 //        }
34409 //
34410 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34411 //        
34412 //    },
34413     
34414     _horizontalLayoutItems : function ( items , isInstant)
34415     {
34416         if ( !items || !items.length || items.length < 3) {
34417             return;
34418         }
34419         
34420         items.reverse();
34421         
34422         var eItems = items.slice(0, 3);
34423         
34424         items = items.slice(3, items.length);
34425         
34426         var standard = [
34427             ['xs', 'xs', 'xs', 'wide'],
34428             ['xs', 'xs', 'wide'],
34429             ['xs', 'xs', 'sm'],
34430             ['xs', 'xs', 'xs'],
34431             ['xs', 'wide'],
34432             ['xs', 'sm'],
34433             ['xs', 'xs'],
34434             ['xs'],
34435             
34436             ['sm', 'xs', 'xs'],
34437             ['sm', 'xs'],
34438             ['sm'],
34439             
34440             ['wide', 'xs', 'xs', 'xs'],
34441             ['wide', 'xs', 'xs'],
34442             ['wide', 'xs'],
34443             ['wide'],
34444             
34445             ['wide-thin']
34446         ];
34447         
34448         var queue = [];
34449         
34450         var boxes = [];
34451         
34452         var box = [];
34453         
34454         Roo.each(items, function(item, k){
34455             
34456             switch (item.size) {
34457                 case 'md' :
34458                 case 'md-left' :
34459                 case 'md-right' :
34460                 case 'tall' :
34461                     
34462                     if(box.length){
34463                         boxes.push(box);
34464                         box = [];
34465                     }
34466                     
34467                     boxes.push([item]);
34468                     
34469                     break;
34470                     
34471                 case 'xs' :
34472                 case 'sm' :
34473                 case 'wide' :
34474                 case 'wide-thin' :
34475                     
34476                     box.push(item);
34477                     
34478                     break;
34479                 default :
34480                     break;
34481                     
34482             }
34483             
34484         }, this);
34485         
34486         if(box.length){
34487             boxes.push(box);
34488             box = [];
34489         }
34490         
34491         var filterPattern = function(box, length)
34492         {
34493             if(!box.length){
34494                 return;
34495             }
34496             
34497             var match = false;
34498             
34499             var pattern = box.slice(0, length);
34500             
34501             var format = [];
34502             
34503             Roo.each(pattern, function(i){
34504                 format.push(i.size);
34505             }, this);
34506             
34507             Roo.each(standard, function(s){
34508                 
34509                 if(String(s) != String(format)){
34510                     return;
34511                 }
34512                 
34513                 match = true;
34514                 return false;
34515                 
34516             }, this);
34517             
34518             if(!match && length == 1){
34519                 return;
34520             }
34521             
34522             if(!match){
34523                 filterPattern(box, length - 1);
34524                 return;
34525             }
34526                 
34527             queue.push(pattern);
34528
34529             box = box.slice(length, box.length);
34530
34531             filterPattern(box, 4);
34532
34533             return;
34534             
34535         }
34536         
34537         Roo.each(boxes, function(box, k){
34538             
34539             if(!box.length){
34540                 return;
34541             }
34542             
34543             if(box.length == 1){
34544                 queue.push(box);
34545                 return;
34546             }
34547             
34548             filterPattern(box, 4);
34549             
34550         }, this);
34551         
34552         
34553         var prune = [];
34554         
34555         var pos = this.el.getBox(true);
34556         
34557         var minX = pos.x;
34558         
34559         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34560         
34561         var hit_end = false;
34562         
34563         Roo.each(queue, function(box){
34564             
34565             if(hit_end){
34566                 
34567                 Roo.each(box, function(b){
34568                 
34569                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34570                     b.el.hide();
34571
34572                 }, this);
34573
34574                 return;
34575             }
34576             
34577             var mx = 0;
34578             
34579             Roo.each(box, function(b){
34580                 
34581                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34582                 b.el.show();
34583
34584                 mx = Math.max(mx, b.x);
34585                 
34586             }, this);
34587             
34588             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34589             
34590             if(maxX < minX){
34591                 
34592                 Roo.each(box, function(b){
34593                 
34594                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34595                     b.el.hide();
34596                     
34597                 }, this);
34598                 
34599                 hit_end = true;
34600                 
34601                 return;
34602             }
34603             
34604             prune.push(box);
34605             
34606         }, this);
34607         
34608         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34609     },
34610     
34611     /** Sets position of item in DOM
34612     * @param {Element} item
34613     * @param {Number} x - horizontal position
34614     * @param {Number} y - vertical position
34615     * @param {Boolean} isInstant - disables transitions
34616     */
34617     _processVerticalLayoutQueue : function( queue, isInstant )
34618     {
34619         var pos = this.el.getBox(true);
34620         var x = pos.x;
34621         var y = pos.y;
34622         var maxY = [];
34623         
34624         for (var i = 0; i < this.cols; i++){
34625             maxY[i] = pos.y;
34626         }
34627         
34628         Roo.each(queue, function(box, k){
34629             
34630             var col = k % this.cols;
34631             
34632             Roo.each(box, function(b,kk){
34633                 
34634                 b.el.position('absolute');
34635                 
34636                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34637                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34638                 
34639                 if(b.size == 'md-left' || b.size == 'md-right'){
34640                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34641                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34642                 }
34643                 
34644                 b.el.setWidth(width);
34645                 b.el.setHeight(height);
34646                 // iframe?
34647                 b.el.select('iframe',true).setSize(width,height);
34648                 
34649             }, this);
34650             
34651             for (var i = 0; i < this.cols; i++){
34652                 
34653                 if(maxY[i] < maxY[col]){
34654                     col = i;
34655                     continue;
34656                 }
34657                 
34658                 col = Math.min(col, i);
34659                 
34660             }
34661             
34662             x = pos.x + col * (this.colWidth + this.padWidth);
34663             
34664             y = maxY[col];
34665             
34666             var positions = [];
34667             
34668             switch (box.length){
34669                 case 1 :
34670                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34671                     break;
34672                 case 2 :
34673                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34674                     break;
34675                 case 3 :
34676                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34677                     break;
34678                 case 4 :
34679                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34680                     break;
34681                 default :
34682                     break;
34683             }
34684             
34685             Roo.each(box, function(b,kk){
34686                 
34687                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34688                 
34689                 var sz = b.el.getSize();
34690                 
34691                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34692                 
34693             }, this);
34694             
34695         }, this);
34696         
34697         var mY = 0;
34698         
34699         for (var i = 0; i < this.cols; i++){
34700             mY = Math.max(mY, maxY[i]);
34701         }
34702         
34703         this.el.setHeight(mY - pos.y);
34704         
34705     },
34706     
34707 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34708 //    {
34709 //        var pos = this.el.getBox(true);
34710 //        var x = pos.x;
34711 //        var y = pos.y;
34712 //        var maxX = pos.right;
34713 //        
34714 //        var maxHeight = 0;
34715 //        
34716 //        Roo.each(items, function(item, k){
34717 //            
34718 //            var c = k % 2;
34719 //            
34720 //            item.el.position('absolute');
34721 //                
34722 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34723 //
34724 //            item.el.setWidth(width);
34725 //
34726 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34727 //
34728 //            item.el.setHeight(height);
34729 //            
34730 //            if(c == 0){
34731 //                item.el.setXY([x, y], isInstant ? false : true);
34732 //            } else {
34733 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34734 //            }
34735 //            
34736 //            y = y + height + this.alternativePadWidth;
34737 //            
34738 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34739 //            
34740 //        }, this);
34741 //        
34742 //        this.el.setHeight(maxHeight);
34743 //        
34744 //    },
34745     
34746     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34747     {
34748         var pos = this.el.getBox(true);
34749         
34750         var minX = pos.x;
34751         var minY = pos.y;
34752         
34753         var maxX = pos.right;
34754         
34755         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34756         
34757         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34758         
34759         Roo.each(queue, function(box, k){
34760             
34761             Roo.each(box, function(b, kk){
34762                 
34763                 b.el.position('absolute');
34764                 
34765                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34766                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34767                 
34768                 if(b.size == 'md-left' || b.size == 'md-right'){
34769                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34770                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34771                 }
34772                 
34773                 b.el.setWidth(width);
34774                 b.el.setHeight(height);
34775                 
34776             }, this);
34777             
34778             if(!box.length){
34779                 return;
34780             }
34781             
34782             var positions = [];
34783             
34784             switch (box.length){
34785                 case 1 :
34786                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34787                     break;
34788                 case 2 :
34789                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34790                     break;
34791                 case 3 :
34792                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34793                     break;
34794                 case 4 :
34795                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34796                     break;
34797                 default :
34798                     break;
34799             }
34800             
34801             Roo.each(box, function(b,kk){
34802                 
34803                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34804                 
34805                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34806                 
34807             }, this);
34808             
34809         }, this);
34810         
34811     },
34812     
34813     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34814     {
34815         Roo.each(eItems, function(b,k){
34816             
34817             b.size = (k == 0) ? 'sm' : 'xs';
34818             b.x = (k == 0) ? 2 : 1;
34819             b.y = (k == 0) ? 2 : 1;
34820             
34821             b.el.position('absolute');
34822             
34823             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34824                 
34825             b.el.setWidth(width);
34826             
34827             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34828             
34829             b.el.setHeight(height);
34830             
34831         }, this);
34832
34833         var positions = [];
34834         
34835         positions.push({
34836             x : maxX - this.unitWidth * 2 - this.gutter,
34837             y : minY
34838         });
34839         
34840         positions.push({
34841             x : maxX - this.unitWidth,
34842             y : minY + (this.unitWidth + this.gutter) * 2
34843         });
34844         
34845         positions.push({
34846             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34847             y : minY
34848         });
34849         
34850         Roo.each(eItems, function(b,k){
34851             
34852             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34853
34854         }, this);
34855         
34856     },
34857     
34858     getVerticalOneBoxColPositions : function(x, y, box)
34859     {
34860         var pos = [];
34861         
34862         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34863         
34864         if(box[0].size == 'md-left'){
34865             rand = 0;
34866         }
34867         
34868         if(box[0].size == 'md-right'){
34869             rand = 1;
34870         }
34871         
34872         pos.push({
34873             x : x + (this.unitWidth + this.gutter) * rand,
34874             y : y
34875         });
34876         
34877         return pos;
34878     },
34879     
34880     getVerticalTwoBoxColPositions : function(x, y, box)
34881     {
34882         var pos = [];
34883         
34884         if(box[0].size == 'xs'){
34885             
34886             pos.push({
34887                 x : x,
34888                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34889             });
34890
34891             pos.push({
34892                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34893                 y : y
34894             });
34895             
34896             return pos;
34897             
34898         }
34899         
34900         pos.push({
34901             x : x,
34902             y : y
34903         });
34904
34905         pos.push({
34906             x : x + (this.unitWidth + this.gutter) * 2,
34907             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34908         });
34909         
34910         return pos;
34911         
34912     },
34913     
34914     getVerticalThreeBoxColPositions : function(x, y, box)
34915     {
34916         var pos = [];
34917         
34918         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34919             
34920             pos.push({
34921                 x : x,
34922                 y : y
34923             });
34924
34925             pos.push({
34926                 x : x + (this.unitWidth + this.gutter) * 1,
34927                 y : y
34928             });
34929             
34930             pos.push({
34931                 x : x + (this.unitWidth + this.gutter) * 2,
34932                 y : y
34933             });
34934             
34935             return pos;
34936             
34937         }
34938         
34939         if(box[0].size == 'xs' && box[1].size == 'xs'){
34940             
34941             pos.push({
34942                 x : x,
34943                 y : y
34944             });
34945
34946             pos.push({
34947                 x : x,
34948                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34949             });
34950             
34951             pos.push({
34952                 x : x + (this.unitWidth + this.gutter) * 1,
34953                 y : y
34954             });
34955             
34956             return pos;
34957             
34958         }
34959         
34960         pos.push({
34961             x : x,
34962             y : y
34963         });
34964
34965         pos.push({
34966             x : x + (this.unitWidth + this.gutter) * 2,
34967             y : y
34968         });
34969
34970         pos.push({
34971             x : x + (this.unitWidth + this.gutter) * 2,
34972             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34973         });
34974             
34975         return pos;
34976         
34977     },
34978     
34979     getVerticalFourBoxColPositions : function(x, y, box)
34980     {
34981         var pos = [];
34982         
34983         if(box[0].size == 'xs'){
34984             
34985             pos.push({
34986                 x : x,
34987                 y : y
34988             });
34989
34990             pos.push({
34991                 x : x,
34992                 y : y + (this.unitHeight + this.gutter) * 1
34993             });
34994             
34995             pos.push({
34996                 x : x,
34997                 y : y + (this.unitHeight + this.gutter) * 2
34998             });
34999             
35000             pos.push({
35001                 x : x + (this.unitWidth + this.gutter) * 1,
35002                 y : y
35003             });
35004             
35005             return pos;
35006             
35007         }
35008         
35009         pos.push({
35010             x : x,
35011             y : y
35012         });
35013
35014         pos.push({
35015             x : x + (this.unitWidth + this.gutter) * 2,
35016             y : y
35017         });
35018
35019         pos.push({
35020             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35021             y : y + (this.unitHeight + this.gutter) * 1
35022         });
35023
35024         pos.push({
35025             x : x + (this.unitWidth + this.gutter) * 2,
35026             y : y + (this.unitWidth + this.gutter) * 2
35027         });
35028
35029         return pos;
35030         
35031     },
35032     
35033     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35034     {
35035         var pos = [];
35036         
35037         if(box[0].size == 'md-left'){
35038             pos.push({
35039                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35040                 y : minY
35041             });
35042             
35043             return pos;
35044         }
35045         
35046         if(box[0].size == 'md-right'){
35047             pos.push({
35048                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35049                 y : minY + (this.unitWidth + this.gutter) * 1
35050             });
35051             
35052             return pos;
35053         }
35054         
35055         var rand = Math.floor(Math.random() * (4 - box[0].y));
35056         
35057         pos.push({
35058             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35059             y : minY + (this.unitWidth + this.gutter) * rand
35060         });
35061         
35062         return pos;
35063         
35064     },
35065     
35066     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35067     {
35068         var pos = [];
35069         
35070         if(box[0].size == 'xs'){
35071             
35072             pos.push({
35073                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35074                 y : minY
35075             });
35076
35077             pos.push({
35078                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35079                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35080             });
35081             
35082             return pos;
35083             
35084         }
35085         
35086         pos.push({
35087             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35088             y : minY
35089         });
35090
35091         pos.push({
35092             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35093             y : minY + (this.unitWidth + this.gutter) * 2
35094         });
35095         
35096         return pos;
35097         
35098     },
35099     
35100     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35101     {
35102         var pos = [];
35103         
35104         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35105             
35106             pos.push({
35107                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35108                 y : minY
35109             });
35110
35111             pos.push({
35112                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35113                 y : minY + (this.unitWidth + this.gutter) * 1
35114             });
35115             
35116             pos.push({
35117                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35118                 y : minY + (this.unitWidth + this.gutter) * 2
35119             });
35120             
35121             return pos;
35122             
35123         }
35124         
35125         if(box[0].size == 'xs' && box[1].size == 'xs'){
35126             
35127             pos.push({
35128                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35129                 y : minY
35130             });
35131
35132             pos.push({
35133                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35134                 y : minY
35135             });
35136             
35137             pos.push({
35138                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35139                 y : minY + (this.unitWidth + this.gutter) * 1
35140             });
35141             
35142             return pos;
35143             
35144         }
35145         
35146         pos.push({
35147             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35148             y : minY
35149         });
35150
35151         pos.push({
35152             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35153             y : minY + (this.unitWidth + this.gutter) * 2
35154         });
35155
35156         pos.push({
35157             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35158             y : minY + (this.unitWidth + this.gutter) * 2
35159         });
35160             
35161         return pos;
35162         
35163     },
35164     
35165     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35166     {
35167         var pos = [];
35168         
35169         if(box[0].size == 'xs'){
35170             
35171             pos.push({
35172                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35173                 y : minY
35174             });
35175
35176             pos.push({
35177                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35178                 y : minY
35179             });
35180             
35181             pos.push({
35182                 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),
35183                 y : minY
35184             });
35185             
35186             pos.push({
35187                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35188                 y : minY + (this.unitWidth + this.gutter) * 1
35189             });
35190             
35191             return pos;
35192             
35193         }
35194         
35195         pos.push({
35196             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35197             y : minY
35198         });
35199         
35200         pos.push({
35201             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35202             y : minY + (this.unitWidth + this.gutter) * 2
35203         });
35204         
35205         pos.push({
35206             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35207             y : minY + (this.unitWidth + this.gutter) * 2
35208         });
35209         
35210         pos.push({
35211             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),
35212             y : minY + (this.unitWidth + this.gutter) * 2
35213         });
35214
35215         return pos;
35216         
35217     },
35218     
35219     /**
35220     * remove a Masonry Brick
35221     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35222     */
35223     removeBrick : function(brick_id)
35224     {
35225         if (!brick_id) {
35226             return;
35227         }
35228         
35229         for (var i = 0; i<this.bricks.length; i++) {
35230             if (this.bricks[i].id == brick_id) {
35231                 this.bricks.splice(i,1);
35232                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35233                 this.initial();
35234             }
35235         }
35236     },
35237     
35238     /**
35239     * adds a Masonry Brick
35240     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35241     */
35242     addBrick : function(cfg)
35243     {
35244         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35245         //this.register(cn);
35246         cn.parentId = this.id;
35247         cn.render(this.el);
35248         return cn;
35249     },
35250     
35251     /**
35252     * register a Masonry Brick
35253     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35254     */
35255     
35256     register : function(brick)
35257     {
35258         this.bricks.push(brick);
35259         brick.masonryId = this.id;
35260     },
35261     
35262     /**
35263     * clear all the Masonry Brick
35264     */
35265     clearAll : function()
35266     {
35267         this.bricks = [];
35268         //this.getChildContainer().dom.innerHTML = "";
35269         this.el.dom.innerHTML = '';
35270     },
35271     
35272     getSelected : function()
35273     {
35274         if (!this.selectedBrick) {
35275             return false;
35276         }
35277         
35278         return this.selectedBrick;
35279     }
35280 });
35281
35282 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35283     
35284     groups: {},
35285      /**
35286     * register a Masonry Layout
35287     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35288     */
35289     
35290     register : function(layout)
35291     {
35292         this.groups[layout.id] = layout;
35293     },
35294     /**
35295     * fetch a  Masonry Layout based on the masonry layout ID
35296     * @param {string} the masonry layout to add
35297     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35298     */
35299     
35300     get: function(layout_id) {
35301         if (typeof(this.groups[layout_id]) == 'undefined') {
35302             return false;
35303         }
35304         return this.groups[layout_id] ;
35305     }
35306     
35307     
35308     
35309 });
35310
35311  
35312
35313  /**
35314  *
35315  * This is based on 
35316  * http://masonry.desandro.com
35317  *
35318  * The idea is to render all the bricks based on vertical width...
35319  *
35320  * The original code extends 'outlayer' - we might need to use that....
35321  * 
35322  */
35323
35324
35325 /**
35326  * @class Roo.bootstrap.LayoutMasonryAuto
35327  * @extends Roo.bootstrap.Component
35328  * Bootstrap Layout Masonry class
35329  * 
35330  * @constructor
35331  * Create a new Element
35332  * @param {Object} config The config object
35333  */
35334
35335 Roo.bootstrap.LayoutMasonryAuto = function(config){
35336     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35337 };
35338
35339 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35340     
35341       /**
35342      * @cfg {Boolean} isFitWidth  - resize the width..
35343      */   
35344     isFitWidth : false,  // options..
35345     /**
35346      * @cfg {Boolean} isOriginLeft = left align?
35347      */   
35348     isOriginLeft : true,
35349     /**
35350      * @cfg {Boolean} isOriginTop = top align?
35351      */   
35352     isOriginTop : false,
35353     /**
35354      * @cfg {Boolean} isLayoutInstant = no animation?
35355      */   
35356     isLayoutInstant : false, // needed?
35357     /**
35358      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35359      */   
35360     isResizingContainer : true,
35361     /**
35362      * @cfg {Number} columnWidth  width of the columns 
35363      */   
35364     
35365     columnWidth : 0,
35366     
35367     /**
35368      * @cfg {Number} maxCols maximum number of columns
35369      */   
35370     
35371     maxCols: 0,
35372     /**
35373      * @cfg {Number} padHeight padding below box..
35374      */   
35375     
35376     padHeight : 10, 
35377     
35378     /**
35379      * @cfg {Boolean} isAutoInitial defalut true
35380      */   
35381     
35382     isAutoInitial : true, 
35383     
35384     // private?
35385     gutter : 0,
35386     
35387     containerWidth: 0,
35388     initialColumnWidth : 0,
35389     currentSize : null,
35390     
35391     colYs : null, // array.
35392     maxY : 0,
35393     padWidth: 10,
35394     
35395     
35396     tag: 'div',
35397     cls: '',
35398     bricks: null, //CompositeElement
35399     cols : 0, // array?
35400     // element : null, // wrapped now this.el
35401     _isLayoutInited : null, 
35402     
35403     
35404     getAutoCreate : function(){
35405         
35406         var cfg = {
35407             tag: this.tag,
35408             cls: 'blog-masonary-wrapper ' + this.cls,
35409             cn : {
35410                 cls : 'mas-boxes masonary'
35411             }
35412         };
35413         
35414         return cfg;
35415     },
35416     
35417     getChildContainer: function( )
35418     {
35419         if (this.boxesEl) {
35420             return this.boxesEl;
35421         }
35422         
35423         this.boxesEl = this.el.select('.mas-boxes').first();
35424         
35425         return this.boxesEl;
35426     },
35427     
35428     
35429     initEvents : function()
35430     {
35431         var _this = this;
35432         
35433         if(this.isAutoInitial){
35434             Roo.log('hook children rendered');
35435             this.on('childrenrendered', function() {
35436                 Roo.log('children rendered');
35437                 _this.initial();
35438             } ,this);
35439         }
35440         
35441     },
35442     
35443     initial : function()
35444     {
35445         this.reloadItems();
35446
35447         this.currentSize = this.el.getBox(true);
35448
35449         /// was window resize... - let's see if this works..
35450         Roo.EventManager.onWindowResize(this.resize, this); 
35451
35452         if(!this.isAutoInitial){
35453             this.layout();
35454             return;
35455         }
35456         
35457         this.layout.defer(500,this);
35458     },
35459     
35460     reloadItems: function()
35461     {
35462         this.bricks = this.el.select('.masonry-brick', true);
35463         
35464         this.bricks.each(function(b) {
35465             //Roo.log(b.getSize());
35466             if (!b.attr('originalwidth')) {
35467                 b.attr('originalwidth',  b.getSize().width);
35468             }
35469             
35470         });
35471         
35472         Roo.log(this.bricks.elements.length);
35473     },
35474     
35475     resize : function()
35476     {
35477         Roo.log('resize');
35478         var cs = this.el.getBox(true);
35479         
35480         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35481             Roo.log("no change in with or X");
35482             return;
35483         }
35484         this.currentSize = cs;
35485         this.layout();
35486     },
35487     
35488     layout : function()
35489     {
35490          Roo.log('layout');
35491         this._resetLayout();
35492         //this._manageStamps();
35493       
35494         // don't animate first layout
35495         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35496         this.layoutItems( isInstant );
35497       
35498         // flag for initalized
35499         this._isLayoutInited = true;
35500     },
35501     
35502     layoutItems : function( isInstant )
35503     {
35504         //var items = this._getItemsForLayout( this.items );
35505         // original code supports filtering layout items.. we just ignore it..
35506         
35507         this._layoutItems( this.bricks , isInstant );
35508       
35509         this._postLayout();
35510     },
35511     _layoutItems : function ( items , isInstant)
35512     {
35513        //this.fireEvent( 'layout', this, items );
35514     
35515
35516         if ( !items || !items.elements.length ) {
35517           // no items, emit event with empty array
35518             return;
35519         }
35520
35521         var queue = [];
35522         items.each(function(item) {
35523             Roo.log("layout item");
35524             Roo.log(item);
35525             // get x/y object from method
35526             var position = this._getItemLayoutPosition( item );
35527             // enqueue
35528             position.item = item;
35529             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35530             queue.push( position );
35531         }, this);
35532       
35533         this._processLayoutQueue( queue );
35534     },
35535     /** Sets position of item in DOM
35536     * @param {Element} item
35537     * @param {Number} x - horizontal position
35538     * @param {Number} y - vertical position
35539     * @param {Boolean} isInstant - disables transitions
35540     */
35541     _processLayoutQueue : function( queue )
35542     {
35543         for ( var i=0, len = queue.length; i < len; i++ ) {
35544             var obj = queue[i];
35545             obj.item.position('absolute');
35546             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35547         }
35548     },
35549       
35550     
35551     /**
35552     * Any logic you want to do after each layout,
35553     * i.e. size the container
35554     */
35555     _postLayout : function()
35556     {
35557         this.resizeContainer();
35558     },
35559     
35560     resizeContainer : function()
35561     {
35562         if ( !this.isResizingContainer ) {
35563             return;
35564         }
35565         var size = this._getContainerSize();
35566         if ( size ) {
35567             this.el.setSize(size.width,size.height);
35568             this.boxesEl.setSize(size.width,size.height);
35569         }
35570     },
35571     
35572     
35573     
35574     _resetLayout : function()
35575     {
35576         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35577         this.colWidth = this.el.getWidth();
35578         //this.gutter = this.el.getWidth(); 
35579         
35580         this.measureColumns();
35581
35582         // reset column Y
35583         var i = this.cols;
35584         this.colYs = [];
35585         while (i--) {
35586             this.colYs.push( 0 );
35587         }
35588     
35589         this.maxY = 0;
35590     },
35591
35592     measureColumns : function()
35593     {
35594         this.getContainerWidth();
35595       // if columnWidth is 0, default to outerWidth of first item
35596         if ( !this.columnWidth ) {
35597             var firstItem = this.bricks.first();
35598             Roo.log(firstItem);
35599             this.columnWidth  = this.containerWidth;
35600             if (firstItem && firstItem.attr('originalwidth') ) {
35601                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35602             }
35603             // columnWidth fall back to item of first element
35604             Roo.log("set column width?");
35605                         this.initialColumnWidth = this.columnWidth  ;
35606
35607             // if first elem has no width, default to size of container
35608             
35609         }
35610         
35611         
35612         if (this.initialColumnWidth) {
35613             this.columnWidth = this.initialColumnWidth;
35614         }
35615         
35616         
35617             
35618         // column width is fixed at the top - however if container width get's smaller we should
35619         // reduce it...
35620         
35621         // this bit calcs how man columns..
35622             
35623         var columnWidth = this.columnWidth += this.gutter;
35624       
35625         // calculate columns
35626         var containerWidth = this.containerWidth + this.gutter;
35627         
35628         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35629         // fix rounding errors, typically with gutters
35630         var excess = columnWidth - containerWidth % columnWidth;
35631         
35632         
35633         // if overshoot is less than a pixel, round up, otherwise floor it
35634         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35635         cols = Math[ mathMethod ]( cols );
35636         this.cols = Math.max( cols, 1 );
35637         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35638         
35639          // padding positioning..
35640         var totalColWidth = this.cols * this.columnWidth;
35641         var padavail = this.containerWidth - totalColWidth;
35642         // so for 2 columns - we need 3 'pads'
35643         
35644         var padNeeded = (1+this.cols) * this.padWidth;
35645         
35646         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35647         
35648         this.columnWidth += padExtra
35649         //this.padWidth = Math.floor(padavail /  ( this.cols));
35650         
35651         // adjust colum width so that padding is fixed??
35652         
35653         // we have 3 columns ... total = width * 3
35654         // we have X left over... that should be used by 
35655         
35656         //if (this.expandC) {
35657             
35658         //}
35659         
35660         
35661         
35662     },
35663     
35664     getContainerWidth : function()
35665     {
35666        /* // container is parent if fit width
35667         var container = this.isFitWidth ? this.element.parentNode : this.element;
35668         // check that this.size and size are there
35669         // IE8 triggers resize on body size change, so they might not be
35670         
35671         var size = getSize( container );  //FIXME
35672         this.containerWidth = size && size.innerWidth; //FIXME
35673         */
35674          
35675         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35676         
35677     },
35678     
35679     _getItemLayoutPosition : function( item )  // what is item?
35680     {
35681         // we resize the item to our columnWidth..
35682       
35683         item.setWidth(this.columnWidth);
35684         item.autoBoxAdjust  = false;
35685         
35686         var sz = item.getSize();
35687  
35688         // how many columns does this brick span
35689         var remainder = this.containerWidth % this.columnWidth;
35690         
35691         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35692         // round if off by 1 pixel, otherwise use ceil
35693         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35694         colSpan = Math.min( colSpan, this.cols );
35695         
35696         // normally this should be '1' as we dont' currently allow multi width columns..
35697         
35698         var colGroup = this._getColGroup( colSpan );
35699         // get the minimum Y value from the columns
35700         var minimumY = Math.min.apply( Math, colGroup );
35701         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35702         
35703         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35704          
35705         // position the brick
35706         var position = {
35707             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35708             y: this.currentSize.y + minimumY + this.padHeight
35709         };
35710         
35711         Roo.log(position);
35712         // apply setHeight to necessary columns
35713         var setHeight = minimumY + sz.height + this.padHeight;
35714         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35715         
35716         var setSpan = this.cols + 1 - colGroup.length;
35717         for ( var i = 0; i < setSpan; i++ ) {
35718           this.colYs[ shortColIndex + i ] = setHeight ;
35719         }
35720       
35721         return position;
35722     },
35723     
35724     /**
35725      * @param {Number} colSpan - number of columns the element spans
35726      * @returns {Array} colGroup
35727      */
35728     _getColGroup : function( colSpan )
35729     {
35730         if ( colSpan < 2 ) {
35731           // if brick spans only one column, use all the column Ys
35732           return this.colYs;
35733         }
35734       
35735         var colGroup = [];
35736         // how many different places could this brick fit horizontally
35737         var groupCount = this.cols + 1 - colSpan;
35738         // for each group potential horizontal position
35739         for ( var i = 0; i < groupCount; i++ ) {
35740           // make an array of colY values for that one group
35741           var groupColYs = this.colYs.slice( i, i + colSpan );
35742           // and get the max value of the array
35743           colGroup[i] = Math.max.apply( Math, groupColYs );
35744         }
35745         return colGroup;
35746     },
35747     /*
35748     _manageStamp : function( stamp )
35749     {
35750         var stampSize =  stamp.getSize();
35751         var offset = stamp.getBox();
35752         // get the columns that this stamp affects
35753         var firstX = this.isOriginLeft ? offset.x : offset.right;
35754         var lastX = firstX + stampSize.width;
35755         var firstCol = Math.floor( firstX / this.columnWidth );
35756         firstCol = Math.max( 0, firstCol );
35757         
35758         var lastCol = Math.floor( lastX / this.columnWidth );
35759         // lastCol should not go over if multiple of columnWidth #425
35760         lastCol -= lastX % this.columnWidth ? 0 : 1;
35761         lastCol = Math.min( this.cols - 1, lastCol );
35762         
35763         // set colYs to bottom of the stamp
35764         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35765             stampSize.height;
35766             
35767         for ( var i = firstCol; i <= lastCol; i++ ) {
35768           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35769         }
35770     },
35771     */
35772     
35773     _getContainerSize : function()
35774     {
35775         this.maxY = Math.max.apply( Math, this.colYs );
35776         var size = {
35777             height: this.maxY
35778         };
35779       
35780         if ( this.isFitWidth ) {
35781             size.width = this._getContainerFitWidth();
35782         }
35783       
35784         return size;
35785     },
35786     
35787     _getContainerFitWidth : function()
35788     {
35789         var unusedCols = 0;
35790         // count unused columns
35791         var i = this.cols;
35792         while ( --i ) {
35793           if ( this.colYs[i] !== 0 ) {
35794             break;
35795           }
35796           unusedCols++;
35797         }
35798         // fit container to columns that have been used
35799         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35800     },
35801     
35802     needsResizeLayout : function()
35803     {
35804         var previousWidth = this.containerWidth;
35805         this.getContainerWidth();
35806         return previousWidth !== this.containerWidth;
35807     }
35808  
35809 });
35810
35811  
35812
35813  /*
35814  * - LGPL
35815  *
35816  * element
35817  * 
35818  */
35819
35820 /**
35821  * @class Roo.bootstrap.MasonryBrick
35822  * @extends Roo.bootstrap.Component
35823  * Bootstrap MasonryBrick class
35824  * 
35825  * @constructor
35826  * Create a new MasonryBrick
35827  * @param {Object} config The config object
35828  */
35829
35830 Roo.bootstrap.MasonryBrick = function(config){
35831     
35832     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35833     
35834     Roo.bootstrap.MasonryBrick.register(this);
35835     
35836     this.addEvents({
35837         // raw events
35838         /**
35839          * @event click
35840          * When a MasonryBrick is clcik
35841          * @param {Roo.bootstrap.MasonryBrick} this
35842          * @param {Roo.EventObject} e
35843          */
35844         "click" : true
35845     });
35846 };
35847
35848 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35849     
35850     /**
35851      * @cfg {String} title
35852      */   
35853     title : '',
35854     /**
35855      * @cfg {String} html
35856      */   
35857     html : '',
35858     /**
35859      * @cfg {String} bgimage
35860      */   
35861     bgimage : '',
35862     /**
35863      * @cfg {String} videourl
35864      */   
35865     videourl : '',
35866     /**
35867      * @cfg {String} cls
35868      */   
35869     cls : '',
35870     /**
35871      * @cfg {String} href
35872      */   
35873     href : '',
35874     /**
35875      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35876      */   
35877     size : 'xs',
35878     
35879     /**
35880      * @cfg {String} placetitle (center|bottom)
35881      */   
35882     placetitle : '',
35883     
35884     /**
35885      * @cfg {Boolean} isFitContainer defalut true
35886      */   
35887     isFitContainer : true, 
35888     
35889     /**
35890      * @cfg {Boolean} preventDefault defalut false
35891      */   
35892     preventDefault : false, 
35893     
35894     /**
35895      * @cfg {Boolean} inverse defalut false
35896      */   
35897     maskInverse : false, 
35898     
35899     getAutoCreate : function()
35900     {
35901         if(!this.isFitContainer){
35902             return this.getSplitAutoCreate();
35903         }
35904         
35905         var cls = 'masonry-brick masonry-brick-full';
35906         
35907         if(this.href.length){
35908             cls += ' masonry-brick-link';
35909         }
35910         
35911         if(this.bgimage.length){
35912             cls += ' masonry-brick-image';
35913         }
35914         
35915         if(this.maskInverse){
35916             cls += ' mask-inverse';
35917         }
35918         
35919         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35920             cls += ' enable-mask';
35921         }
35922         
35923         if(this.size){
35924             cls += ' masonry-' + this.size + '-brick';
35925         }
35926         
35927         if(this.placetitle.length){
35928             
35929             switch (this.placetitle) {
35930                 case 'center' :
35931                     cls += ' masonry-center-title';
35932                     break;
35933                 case 'bottom' :
35934                     cls += ' masonry-bottom-title';
35935                     break;
35936                 default:
35937                     break;
35938             }
35939             
35940         } else {
35941             if(!this.html.length && !this.bgimage.length){
35942                 cls += ' masonry-center-title';
35943             }
35944
35945             if(!this.html.length && this.bgimage.length){
35946                 cls += ' masonry-bottom-title';
35947             }
35948         }
35949         
35950         if(this.cls){
35951             cls += ' ' + this.cls;
35952         }
35953         
35954         var cfg = {
35955             tag: (this.href.length) ? 'a' : 'div',
35956             cls: cls,
35957             cn: [
35958                 {
35959                     tag: 'div',
35960                     cls: 'masonry-brick-mask'
35961                 },
35962                 {
35963                     tag: 'div',
35964                     cls: 'masonry-brick-paragraph',
35965                     cn: []
35966                 }
35967             ]
35968         };
35969         
35970         if(this.href.length){
35971             cfg.href = this.href;
35972         }
35973         
35974         var cn = cfg.cn[1].cn;
35975         
35976         if(this.title.length){
35977             cn.push({
35978                 tag: 'h4',
35979                 cls: 'masonry-brick-title',
35980                 html: this.title
35981             });
35982         }
35983         
35984         if(this.html.length){
35985             cn.push({
35986                 tag: 'p',
35987                 cls: 'masonry-brick-text',
35988                 html: this.html
35989             });
35990         }
35991         
35992         if (!this.title.length && !this.html.length) {
35993             cfg.cn[1].cls += ' hide';
35994         }
35995         
35996         if(this.bgimage.length){
35997             cfg.cn.push({
35998                 tag: 'img',
35999                 cls: 'masonry-brick-image-view',
36000                 src: this.bgimage
36001             });
36002         }
36003         
36004         if(this.videourl.length){
36005             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36006             // youtube support only?
36007             cfg.cn.push({
36008                 tag: 'iframe',
36009                 cls: 'masonry-brick-image-view',
36010                 src: vurl,
36011                 frameborder : 0,
36012                 allowfullscreen : true
36013             });
36014         }
36015         
36016         return cfg;
36017         
36018     },
36019     
36020     getSplitAutoCreate : function()
36021     {
36022         var cls = 'masonry-brick masonry-brick-split';
36023         
36024         if(this.href.length){
36025             cls += ' masonry-brick-link';
36026         }
36027         
36028         if(this.bgimage.length){
36029             cls += ' masonry-brick-image';
36030         }
36031         
36032         if(this.size){
36033             cls += ' masonry-' + this.size + '-brick';
36034         }
36035         
36036         switch (this.placetitle) {
36037             case 'center' :
36038                 cls += ' masonry-center-title';
36039                 break;
36040             case 'bottom' :
36041                 cls += ' masonry-bottom-title';
36042                 break;
36043             default:
36044                 if(!this.bgimage.length){
36045                     cls += ' masonry-center-title';
36046                 }
36047
36048                 if(this.bgimage.length){
36049                     cls += ' masonry-bottom-title';
36050                 }
36051                 break;
36052         }
36053         
36054         if(this.cls){
36055             cls += ' ' + this.cls;
36056         }
36057         
36058         var cfg = {
36059             tag: (this.href.length) ? 'a' : 'div',
36060             cls: cls,
36061             cn: [
36062                 {
36063                     tag: 'div',
36064                     cls: 'masonry-brick-split-head',
36065                     cn: [
36066                         {
36067                             tag: 'div',
36068                             cls: 'masonry-brick-paragraph',
36069                             cn: []
36070                         }
36071                     ]
36072                 },
36073                 {
36074                     tag: 'div',
36075                     cls: 'masonry-brick-split-body',
36076                     cn: []
36077                 }
36078             ]
36079         };
36080         
36081         if(this.href.length){
36082             cfg.href = this.href;
36083         }
36084         
36085         if(this.title.length){
36086             cfg.cn[0].cn[0].cn.push({
36087                 tag: 'h4',
36088                 cls: 'masonry-brick-title',
36089                 html: this.title
36090             });
36091         }
36092         
36093         if(this.html.length){
36094             cfg.cn[1].cn.push({
36095                 tag: 'p',
36096                 cls: 'masonry-brick-text',
36097                 html: this.html
36098             });
36099         }
36100
36101         if(this.bgimage.length){
36102             cfg.cn[0].cn.push({
36103                 tag: 'img',
36104                 cls: 'masonry-brick-image-view',
36105                 src: this.bgimage
36106             });
36107         }
36108         
36109         if(this.videourl.length){
36110             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36111             // youtube support only?
36112             cfg.cn[0].cn.cn.push({
36113                 tag: 'iframe',
36114                 cls: 'masonry-brick-image-view',
36115                 src: vurl,
36116                 frameborder : 0,
36117                 allowfullscreen : true
36118             });
36119         }
36120         
36121         return cfg;
36122     },
36123     
36124     initEvents: function() 
36125     {
36126         switch (this.size) {
36127             case 'xs' :
36128                 this.x = 1;
36129                 this.y = 1;
36130                 break;
36131             case 'sm' :
36132                 this.x = 2;
36133                 this.y = 2;
36134                 break;
36135             case 'md' :
36136             case 'md-left' :
36137             case 'md-right' :
36138                 this.x = 3;
36139                 this.y = 3;
36140                 break;
36141             case 'tall' :
36142                 this.x = 2;
36143                 this.y = 3;
36144                 break;
36145             case 'wide' :
36146                 this.x = 3;
36147                 this.y = 2;
36148                 break;
36149             case 'wide-thin' :
36150                 this.x = 3;
36151                 this.y = 1;
36152                 break;
36153                         
36154             default :
36155                 break;
36156         }
36157         
36158         if(Roo.isTouch){
36159             this.el.on('touchstart', this.onTouchStart, this);
36160             this.el.on('touchmove', this.onTouchMove, this);
36161             this.el.on('touchend', this.onTouchEnd, this);
36162             this.el.on('contextmenu', this.onContextMenu, this);
36163         } else {
36164             this.el.on('mouseenter'  ,this.enter, this);
36165             this.el.on('mouseleave', this.leave, this);
36166             this.el.on('click', this.onClick, this);
36167         }
36168         
36169         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36170             this.parent().bricks.push(this);   
36171         }
36172         
36173     },
36174     
36175     onClick: function(e, el)
36176     {
36177         var time = this.endTimer - this.startTimer;
36178         // Roo.log(e.preventDefault());
36179         if(Roo.isTouch){
36180             if(time > 1000){
36181                 e.preventDefault();
36182                 return;
36183             }
36184         }
36185         
36186         if(!this.preventDefault){
36187             return;
36188         }
36189         
36190         e.preventDefault();
36191         
36192         if (this.activeClass != '') {
36193             this.selectBrick();
36194         }
36195         
36196         this.fireEvent('click', this, e);
36197     },
36198     
36199     enter: function(e, el)
36200     {
36201         e.preventDefault();
36202         
36203         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36204             return;
36205         }
36206         
36207         if(this.bgimage.length && this.html.length){
36208             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36209         }
36210     },
36211     
36212     leave: function(e, el)
36213     {
36214         e.preventDefault();
36215         
36216         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36217             return;
36218         }
36219         
36220         if(this.bgimage.length && this.html.length){
36221             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36222         }
36223     },
36224     
36225     onTouchStart: function(e, el)
36226     {
36227 //        e.preventDefault();
36228         
36229         this.touchmoved = false;
36230         
36231         if(!this.isFitContainer){
36232             return;
36233         }
36234         
36235         if(!this.bgimage.length || !this.html.length){
36236             return;
36237         }
36238         
36239         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36240         
36241         this.timer = new Date().getTime();
36242         
36243     },
36244     
36245     onTouchMove: function(e, el)
36246     {
36247         this.touchmoved = true;
36248     },
36249     
36250     onContextMenu : function(e,el)
36251     {
36252         e.preventDefault();
36253         e.stopPropagation();
36254         return false;
36255     },
36256     
36257     onTouchEnd: function(e, el)
36258     {
36259 //        e.preventDefault();
36260         
36261         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36262         
36263             this.leave(e,el);
36264             
36265             return;
36266         }
36267         
36268         if(!this.bgimage.length || !this.html.length){
36269             
36270             if(this.href.length){
36271                 window.location.href = this.href;
36272             }
36273             
36274             return;
36275         }
36276         
36277         if(!this.isFitContainer){
36278             return;
36279         }
36280         
36281         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36282         
36283         window.location.href = this.href;
36284     },
36285     
36286     //selection on single brick only
36287     selectBrick : function() {
36288         
36289         if (!this.parentId) {
36290             return;
36291         }
36292         
36293         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36294         var index = m.selectedBrick.indexOf(this.id);
36295         
36296         if ( index > -1) {
36297             m.selectedBrick.splice(index,1);
36298             this.el.removeClass(this.activeClass);
36299             return;
36300         }
36301         
36302         for(var i = 0; i < m.selectedBrick.length; i++) {
36303             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36304             b.el.removeClass(b.activeClass);
36305         }
36306         
36307         m.selectedBrick = [];
36308         
36309         m.selectedBrick.push(this.id);
36310         this.el.addClass(this.activeClass);
36311         return;
36312     },
36313     
36314     isSelected : function(){
36315         return this.el.hasClass(this.activeClass);
36316         
36317     }
36318 });
36319
36320 Roo.apply(Roo.bootstrap.MasonryBrick, {
36321     
36322     //groups: {},
36323     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36324      /**
36325     * register a Masonry Brick
36326     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36327     */
36328     
36329     register : function(brick)
36330     {
36331         //this.groups[brick.id] = brick;
36332         this.groups.add(brick.id, brick);
36333     },
36334     /**
36335     * fetch a  masonry brick based on the masonry brick ID
36336     * @param {string} the masonry brick to add
36337     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36338     */
36339     
36340     get: function(brick_id) 
36341     {
36342         // if (typeof(this.groups[brick_id]) == 'undefined') {
36343         //     return false;
36344         // }
36345         // return this.groups[brick_id] ;
36346         
36347         if(this.groups.key(brick_id)) {
36348             return this.groups.key(brick_id);
36349         }
36350         
36351         return false;
36352     }
36353     
36354     
36355     
36356 });
36357
36358  /*
36359  * - LGPL
36360  *
36361  * element
36362  * 
36363  */
36364
36365 /**
36366  * @class Roo.bootstrap.Brick
36367  * @extends Roo.bootstrap.Component
36368  * Bootstrap Brick class
36369  * 
36370  * @constructor
36371  * Create a new Brick
36372  * @param {Object} config The config object
36373  */
36374
36375 Roo.bootstrap.Brick = function(config){
36376     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36377     
36378     this.addEvents({
36379         // raw events
36380         /**
36381          * @event click
36382          * When a Brick is click
36383          * @param {Roo.bootstrap.Brick} this
36384          * @param {Roo.EventObject} e
36385          */
36386         "click" : true
36387     });
36388 };
36389
36390 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36391     
36392     /**
36393      * @cfg {String} title
36394      */   
36395     title : '',
36396     /**
36397      * @cfg {String} html
36398      */   
36399     html : '',
36400     /**
36401      * @cfg {String} bgimage
36402      */   
36403     bgimage : '',
36404     /**
36405      * @cfg {String} cls
36406      */   
36407     cls : '',
36408     /**
36409      * @cfg {String} href
36410      */   
36411     href : '',
36412     /**
36413      * @cfg {String} video
36414      */   
36415     video : '',
36416     /**
36417      * @cfg {Boolean} square
36418      */   
36419     square : true,
36420     
36421     getAutoCreate : function()
36422     {
36423         var cls = 'roo-brick';
36424         
36425         if(this.href.length){
36426             cls += ' roo-brick-link';
36427         }
36428         
36429         if(this.bgimage.length){
36430             cls += ' roo-brick-image';
36431         }
36432         
36433         if(!this.html.length && !this.bgimage.length){
36434             cls += ' roo-brick-center-title';
36435         }
36436         
36437         if(!this.html.length && this.bgimage.length){
36438             cls += ' roo-brick-bottom-title';
36439         }
36440         
36441         if(this.cls){
36442             cls += ' ' + this.cls;
36443         }
36444         
36445         var cfg = {
36446             tag: (this.href.length) ? 'a' : 'div',
36447             cls: cls,
36448             cn: [
36449                 {
36450                     tag: 'div',
36451                     cls: 'roo-brick-paragraph',
36452                     cn: []
36453                 }
36454             ]
36455         };
36456         
36457         if(this.href.length){
36458             cfg.href = this.href;
36459         }
36460         
36461         var cn = cfg.cn[0].cn;
36462         
36463         if(this.title.length){
36464             cn.push({
36465                 tag: 'h4',
36466                 cls: 'roo-brick-title',
36467                 html: this.title
36468             });
36469         }
36470         
36471         if(this.html.length){
36472             cn.push({
36473                 tag: 'p',
36474                 cls: 'roo-brick-text',
36475                 html: this.html
36476             });
36477         } else {
36478             cn.cls += ' hide';
36479         }
36480         
36481         if(this.bgimage.length){
36482             cfg.cn.push({
36483                 tag: 'img',
36484                 cls: 'roo-brick-image-view',
36485                 src: this.bgimage
36486             });
36487         }
36488         
36489         return cfg;
36490     },
36491     
36492     initEvents: function() 
36493     {
36494         if(this.title.length || this.html.length){
36495             this.el.on('mouseenter'  ,this.enter, this);
36496             this.el.on('mouseleave', this.leave, this);
36497         }
36498         
36499         Roo.EventManager.onWindowResize(this.resize, this); 
36500         
36501         if(this.bgimage.length){
36502             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36503             this.imageEl.on('load', this.onImageLoad, this);
36504             return;
36505         }
36506         
36507         this.resize();
36508     },
36509     
36510     onImageLoad : function()
36511     {
36512         this.resize();
36513     },
36514     
36515     resize : function()
36516     {
36517         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36518         
36519         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36520         
36521         if(this.bgimage.length){
36522             var image = this.el.select('.roo-brick-image-view', true).first();
36523             
36524             image.setWidth(paragraph.getWidth());
36525             
36526             if(this.square){
36527                 image.setHeight(paragraph.getWidth());
36528             }
36529             
36530             this.el.setHeight(image.getHeight());
36531             paragraph.setHeight(image.getHeight());
36532             
36533         }
36534         
36535     },
36536     
36537     enter: function(e, el)
36538     {
36539         e.preventDefault();
36540         
36541         if(this.bgimage.length){
36542             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36543             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36544         }
36545     },
36546     
36547     leave: function(e, el)
36548     {
36549         e.preventDefault();
36550         
36551         if(this.bgimage.length){
36552             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36553             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36554         }
36555     }
36556     
36557 });
36558
36559  
36560
36561  /*
36562  * - LGPL
36563  *
36564  * Number field 
36565  */
36566
36567 /**
36568  * @class Roo.bootstrap.form.NumberField
36569  * @extends Roo.bootstrap.form.Input
36570  * Bootstrap NumberField class
36571  * 
36572  * 
36573  * 
36574  * 
36575  * @constructor
36576  * Create a new NumberField
36577  * @param {Object} config The config object
36578  */
36579
36580 Roo.bootstrap.form.NumberField = function(config){
36581     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36582 };
36583
36584 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36585     
36586     /**
36587      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36588      */
36589     allowDecimals : true,
36590     /**
36591      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36592      */
36593     decimalSeparator : ".",
36594     /**
36595      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36596      */
36597     decimalPrecision : 2,
36598     /**
36599      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36600      */
36601     allowNegative : true,
36602     
36603     /**
36604      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36605      */
36606     allowZero: true,
36607     /**
36608      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36609      */
36610     minValue : Number.NEGATIVE_INFINITY,
36611     /**
36612      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36613      */
36614     maxValue : Number.MAX_VALUE,
36615     /**
36616      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36617      */
36618     minText : "The minimum value for this field is {0}",
36619     /**
36620      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36621      */
36622     maxText : "The maximum value for this field is {0}",
36623     /**
36624      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36625      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36626      */
36627     nanText : "{0} is not a valid number",
36628     /**
36629      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36630      */
36631     thousandsDelimiter : false,
36632     /**
36633      * @cfg {String} valueAlign alignment of value
36634      */
36635     valueAlign : "left",
36636
36637     getAutoCreate : function()
36638     {
36639         var hiddenInput = {
36640             tag: 'input',
36641             type: 'hidden',
36642             id: Roo.id(),
36643             cls: 'hidden-number-input'
36644         };
36645         
36646         if (this.name) {
36647             hiddenInput.name = this.name;
36648         }
36649         
36650         this.name = '';
36651         
36652         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36653         
36654         this.name = hiddenInput.name;
36655         
36656         if(cfg.cn.length > 0) {
36657             cfg.cn.push(hiddenInput);
36658         }
36659         
36660         return cfg;
36661     },
36662
36663     // private
36664     initEvents : function()
36665     {   
36666         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36667         
36668         var allowed = "0123456789";
36669         
36670         if(this.allowDecimals){
36671             allowed += this.decimalSeparator;
36672         }
36673         
36674         if(this.allowNegative){
36675             allowed += "-";
36676         }
36677         
36678         if(this.thousandsDelimiter) {
36679             allowed += ",";
36680         }
36681         
36682         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36683         
36684         var keyPress = function(e){
36685             
36686             var k = e.getKey();
36687             
36688             var c = e.getCharCode();
36689             
36690             if(
36691                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36692                     allowed.indexOf(String.fromCharCode(c)) === -1
36693             ){
36694                 e.stopEvent();
36695                 return;
36696             }
36697             
36698             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36699                 return;
36700             }
36701             
36702             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36703                 e.stopEvent();
36704             }
36705         };
36706         
36707         this.el.on("keypress", keyPress, this);
36708     },
36709     
36710     validateValue : function(value)
36711     {
36712         
36713         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36714             return false;
36715         }
36716         
36717         var num = this.parseValue(value);
36718         
36719         if(isNaN(num)){
36720             this.markInvalid(String.format(this.nanText, value));
36721             return false;
36722         }
36723         
36724         if(num < this.minValue){
36725             this.markInvalid(String.format(this.minText, this.minValue));
36726             return false;
36727         }
36728         
36729         if(num > this.maxValue){
36730             this.markInvalid(String.format(this.maxText, this.maxValue));
36731             return false;
36732         }
36733         
36734         return true;
36735     },
36736
36737     getValue : function()
36738     {
36739         var v = this.hiddenEl().getValue();
36740         
36741         return this.fixPrecision(this.parseValue(v));
36742     },
36743
36744     parseValue : function(value)
36745     {
36746         if(this.thousandsDelimiter) {
36747             value += "";
36748             r = new RegExp(",", "g");
36749             value = value.replace(r, "");
36750         }
36751         
36752         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36753         return isNaN(value) ? '' : value;
36754     },
36755
36756     fixPrecision : function(value)
36757     {
36758         if(this.thousandsDelimiter) {
36759             value += "";
36760             r = new RegExp(",", "g");
36761             value = value.replace(r, "");
36762         }
36763         
36764         var nan = isNaN(value);
36765         
36766         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36767             return nan ? '' : value;
36768         }
36769         return parseFloat(value).toFixed(this.decimalPrecision);
36770     },
36771
36772     setValue : function(v)
36773     {
36774         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36775         
36776         this.value = v;
36777         
36778         if(this.rendered){
36779             
36780             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36781             
36782             this.inputEl().dom.value = (v == '') ? '' :
36783                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36784             
36785             if(!this.allowZero && v === '0') {
36786                 this.hiddenEl().dom.value = '';
36787                 this.inputEl().dom.value = '';
36788             }
36789             
36790             this.validate();
36791         }
36792     },
36793
36794     decimalPrecisionFcn : function(v)
36795     {
36796         return Math.floor(v);
36797     },
36798
36799     beforeBlur : function()
36800     {
36801         var v = this.parseValue(this.getRawValue());
36802         
36803         if(v || v === 0 || v === ''){
36804             this.setValue(v);
36805         }
36806     },
36807     
36808     hiddenEl : function()
36809     {
36810         return this.el.select('input.hidden-number-input',true).first();
36811     }
36812     
36813 });
36814
36815  
36816
36817 /*
36818 * Licence: LGPL
36819 */
36820
36821 /**
36822  * @class Roo.bootstrap.DocumentSlider
36823  * @extends Roo.bootstrap.Component
36824  * Bootstrap DocumentSlider class
36825  * 
36826  * @constructor
36827  * Create a new DocumentViewer
36828  * @param {Object} config The config object
36829  */
36830
36831 Roo.bootstrap.DocumentSlider = function(config){
36832     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36833     
36834     this.files = [];
36835     
36836     this.addEvents({
36837         /**
36838          * @event initial
36839          * Fire after initEvent
36840          * @param {Roo.bootstrap.DocumentSlider} this
36841          */
36842         "initial" : true,
36843         /**
36844          * @event update
36845          * Fire after update
36846          * @param {Roo.bootstrap.DocumentSlider} this
36847          */
36848         "update" : true,
36849         /**
36850          * @event click
36851          * Fire after click
36852          * @param {Roo.bootstrap.DocumentSlider} this
36853          */
36854         "click" : true
36855     });
36856 };
36857
36858 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36859     
36860     files : false,
36861     
36862     indicator : 0,
36863     
36864     getAutoCreate : function()
36865     {
36866         var cfg = {
36867             tag : 'div',
36868             cls : 'roo-document-slider',
36869             cn : [
36870                 {
36871                     tag : 'div',
36872                     cls : 'roo-document-slider-header',
36873                     cn : [
36874                         {
36875                             tag : 'div',
36876                             cls : 'roo-document-slider-header-title'
36877                         }
36878                     ]
36879                 },
36880                 {
36881                     tag : 'div',
36882                     cls : 'roo-document-slider-body',
36883                     cn : [
36884                         {
36885                             tag : 'div',
36886                             cls : 'roo-document-slider-prev',
36887                             cn : [
36888                                 {
36889                                     tag : 'i',
36890                                     cls : 'fa fa-chevron-left'
36891                                 }
36892                             ]
36893                         },
36894                         {
36895                             tag : 'div',
36896                             cls : 'roo-document-slider-thumb',
36897                             cn : [
36898                                 {
36899                                     tag : 'img',
36900                                     cls : 'roo-document-slider-image'
36901                                 }
36902                             ]
36903                         },
36904                         {
36905                             tag : 'div',
36906                             cls : 'roo-document-slider-next',
36907                             cn : [
36908                                 {
36909                                     tag : 'i',
36910                                     cls : 'fa fa-chevron-right'
36911                                 }
36912                             ]
36913                         }
36914                     ]
36915                 }
36916             ]
36917         };
36918         
36919         return cfg;
36920     },
36921     
36922     initEvents : function()
36923     {
36924         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36925         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36926         
36927         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36928         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36929         
36930         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36931         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36932         
36933         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36934         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36935         
36936         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36937         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36938         
36939         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36940         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36941         
36942         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36943         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36944         
36945         this.thumbEl.on('click', this.onClick, this);
36946         
36947         this.prevIndicator.on('click', this.prev, this);
36948         
36949         this.nextIndicator.on('click', this.next, this);
36950         
36951     },
36952     
36953     initial : function()
36954     {
36955         if(this.files.length){
36956             this.indicator = 1;
36957             this.update()
36958         }
36959         
36960         this.fireEvent('initial', this);
36961     },
36962     
36963     update : function()
36964     {
36965         this.imageEl.attr('src', this.files[this.indicator - 1]);
36966         
36967         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36968         
36969         this.prevIndicator.show();
36970         
36971         if(this.indicator == 1){
36972             this.prevIndicator.hide();
36973         }
36974         
36975         this.nextIndicator.show();
36976         
36977         if(this.indicator == this.files.length){
36978             this.nextIndicator.hide();
36979         }
36980         
36981         this.thumbEl.scrollTo('top');
36982         
36983         this.fireEvent('update', this);
36984     },
36985     
36986     onClick : function(e)
36987     {
36988         e.preventDefault();
36989         
36990         this.fireEvent('click', this);
36991     },
36992     
36993     prev : function(e)
36994     {
36995         e.preventDefault();
36996         
36997         this.indicator = Math.max(1, this.indicator - 1);
36998         
36999         this.update();
37000     },
37001     
37002     next : function(e)
37003     {
37004         e.preventDefault();
37005         
37006         this.indicator = Math.min(this.files.length, this.indicator + 1);
37007         
37008         this.update();
37009     }
37010 });
37011 /*
37012  * - LGPL
37013  *
37014  * RadioSet
37015  *
37016  *
37017  */
37018
37019 /**
37020  * @class Roo.bootstrap.form.RadioSet
37021  * @extends Roo.bootstrap.form.Input
37022  * @children Roo.bootstrap.form.Radio
37023  * Bootstrap RadioSet class
37024  * @cfg {String} indicatorpos (left|right) default left
37025  * @cfg {Boolean} inline (true|false) inline the element (default true)
37026  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37027  * @constructor
37028  * Create a new RadioSet
37029  * @param {Object} config The config object
37030  */
37031
37032 Roo.bootstrap.form.RadioSet = function(config){
37033     
37034     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37035     
37036     this.radioes = [];
37037     
37038     Roo.bootstrap.form.RadioSet.register(this);
37039     
37040     this.addEvents({
37041         /**
37042         * @event check
37043         * Fires when the element is checked or unchecked.
37044         * @param {Roo.bootstrap.form.RadioSet} this This radio
37045         * @param {Roo.bootstrap.form.Radio} item The checked item
37046         */
37047        check : true,
37048        /**
37049         * @event click
37050         * Fires when the element is click.
37051         * @param {Roo.bootstrap.form.RadioSet} this This radio set
37052         * @param {Roo.bootstrap.form.Radio} item The checked item
37053         * @param {Roo.EventObject} e The event object
37054         */
37055        click : true
37056     });
37057     
37058 };
37059
37060 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
37061
37062     radioes : false,
37063     
37064     inline : true,
37065     
37066     weight : '',
37067     
37068     indicatorpos : 'left',
37069     
37070     getAutoCreate : function()
37071     {
37072         var label = {
37073             tag : 'label',
37074             cls : 'roo-radio-set-label',
37075             cn : [
37076                 {
37077                     tag : 'span',
37078                     html : this.fieldLabel
37079                 }
37080             ]
37081         };
37082         if (Roo.bootstrap.version == 3) {
37083             
37084             
37085             if(this.indicatorpos == 'left'){
37086                 label.cn.unshift({
37087                     tag : 'i',
37088                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37089                     tooltip : 'This field is required'
37090                 });
37091             } else {
37092                 label.cn.push({
37093                     tag : 'i',
37094                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37095                     tooltip : 'This field is required'
37096                 });
37097             }
37098         }
37099         var items = {
37100             tag : 'div',
37101             cls : 'roo-radio-set-items'
37102         };
37103         
37104         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37105         
37106         if (align === 'left' && this.fieldLabel.length) {
37107             
37108             items = {
37109                 cls : "roo-radio-set-right", 
37110                 cn: [
37111                     items
37112                 ]
37113             };
37114             
37115             if(this.labelWidth > 12){
37116                 label.style = "width: " + this.labelWidth + 'px';
37117             }
37118             
37119             if(this.labelWidth < 13 && this.labelmd == 0){
37120                 this.labelmd = this.labelWidth;
37121             }
37122             
37123             if(this.labellg > 0){
37124                 label.cls += ' col-lg-' + this.labellg;
37125                 items.cls += ' col-lg-' + (12 - this.labellg);
37126             }
37127             
37128             if(this.labelmd > 0){
37129                 label.cls += ' col-md-' + this.labelmd;
37130                 items.cls += ' col-md-' + (12 - this.labelmd);
37131             }
37132             
37133             if(this.labelsm > 0){
37134                 label.cls += ' col-sm-' + this.labelsm;
37135                 items.cls += ' col-sm-' + (12 - this.labelsm);
37136             }
37137             
37138             if(this.labelxs > 0){
37139                 label.cls += ' col-xs-' + this.labelxs;
37140                 items.cls += ' col-xs-' + (12 - this.labelxs);
37141             }
37142         }
37143         
37144         var cfg = {
37145             tag : 'div',
37146             cls : 'roo-radio-set',
37147             cn : [
37148                 {
37149                     tag : 'input',
37150                     cls : 'roo-radio-set-input',
37151                     type : 'hidden',
37152                     name : this.name,
37153                     value : this.value ? this.value :  ''
37154                 },
37155                 label,
37156                 items
37157             ]
37158         };
37159         
37160         if(this.weight.length){
37161             cfg.cls += ' roo-radio-' + this.weight;
37162         }
37163         
37164         if(this.inline) {
37165             cfg.cls += ' roo-radio-set-inline';
37166         }
37167         
37168         var settings=this;
37169         ['xs','sm','md','lg'].map(function(size){
37170             if (settings[size]) {
37171                 cfg.cls += ' col-' + size + '-' + settings[size];
37172             }
37173         });
37174         
37175         return cfg;
37176         
37177     },
37178
37179     initEvents : function()
37180     {
37181         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37182         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37183         
37184         if(!this.fieldLabel.length){
37185             this.labelEl.hide();
37186         }
37187         
37188         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37189         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37190         
37191         this.indicator = this.indicatorEl();
37192         
37193         if(this.indicator){
37194             this.indicator.addClass('invisible');
37195         }
37196         
37197         this.originalValue = this.getValue();
37198         
37199     },
37200     
37201     inputEl: function ()
37202     {
37203         return this.el.select('.roo-radio-set-input', true).first();
37204     },
37205     
37206     getChildContainer : function()
37207     {
37208         return this.itemsEl;
37209     },
37210     
37211     register : function(item)
37212     {
37213         this.radioes.push(item);
37214         
37215     },
37216     
37217     validate : function()
37218     {   
37219         if(this.getVisibilityEl().hasClass('hidden')){
37220             return true;
37221         }
37222         
37223         var valid = false;
37224         
37225         Roo.each(this.radioes, function(i){
37226             if(!i.checked){
37227                 return;
37228             }
37229             
37230             valid = true;
37231             return false;
37232         });
37233         
37234         if(this.allowBlank) {
37235             return true;
37236         }
37237         
37238         if(this.disabled || valid){
37239             this.markValid();
37240             return true;
37241         }
37242         
37243         this.markInvalid();
37244         return false;
37245         
37246     },
37247     
37248     markValid : function()
37249     {
37250         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37251             this.indicatorEl().removeClass('visible');
37252             this.indicatorEl().addClass('invisible');
37253         }
37254         
37255         
37256         if (Roo.bootstrap.version == 3) {
37257             this.el.removeClass([this.invalidClass, this.validClass]);
37258             this.el.addClass(this.validClass);
37259         } else {
37260             this.el.removeClass(['is-invalid','is-valid']);
37261             this.el.addClass(['is-valid']);
37262         }
37263         this.fireEvent('valid', this);
37264     },
37265     
37266     markInvalid : function(msg)
37267     {
37268         if(this.allowBlank || this.disabled){
37269             return;
37270         }
37271         
37272         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37273             this.indicatorEl().removeClass('invisible');
37274             this.indicatorEl().addClass('visible');
37275         }
37276         if (Roo.bootstrap.version == 3) {
37277             this.el.removeClass([this.invalidClass, this.validClass]);
37278             this.el.addClass(this.invalidClass);
37279         } else {
37280             this.el.removeClass(['is-invalid','is-valid']);
37281             this.el.addClass(['is-invalid']);
37282         }
37283         
37284         this.fireEvent('invalid', this, msg);
37285         
37286     },
37287     
37288     setValue : function(v, suppressEvent)
37289     {   
37290         if(this.value === v){
37291             return;
37292         }
37293         
37294         this.value = v;
37295         
37296         if(this.rendered){
37297             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37298         }
37299         
37300         Roo.each(this.radioes, function(i){
37301             i.checked = false;
37302             i.el.removeClass('checked');
37303         });
37304         
37305         Roo.each(this.radioes, function(i){
37306             
37307             if(i.value === v || i.value.toString() === v.toString()){
37308                 i.checked = true;
37309                 i.el.addClass('checked');
37310                 
37311                 if(suppressEvent !== true){
37312                     this.fireEvent('check', this, i);
37313                 }
37314                 
37315                 return false;
37316             }
37317             
37318         }, this);
37319         
37320         this.validate();
37321     },
37322     
37323     clearInvalid : function(){
37324         
37325         if(!this.el || this.preventMark){
37326             return;
37327         }
37328         
37329         this.el.removeClass([this.invalidClass]);
37330         
37331         this.fireEvent('valid', this);
37332     }
37333     
37334 });
37335
37336 Roo.apply(Roo.bootstrap.form.RadioSet, {
37337     
37338     groups: {},
37339     
37340     register : function(set)
37341     {
37342         this.groups[set.name] = set;
37343     },
37344     
37345     get: function(name) 
37346     {
37347         if (typeof(this.groups[name]) == 'undefined') {
37348             return false;
37349         }
37350         
37351         return this.groups[name] ;
37352     }
37353     
37354 });
37355 /*
37356  * Based on:
37357  * Ext JS Library 1.1.1
37358  * Copyright(c) 2006-2007, Ext JS, LLC.
37359  *
37360  * Originally Released Under LGPL - original licence link has changed is not relivant.
37361  *
37362  * Fork - LGPL
37363  * <script type="text/javascript">
37364  */
37365
37366
37367 /**
37368  * @class Roo.bootstrap.SplitBar
37369  * @extends Roo.util.Observable
37370  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37371  * <br><br>
37372  * Usage:
37373  * <pre><code>
37374 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37375                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37376 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37377 split.minSize = 100;
37378 split.maxSize = 600;
37379 split.animate = true;
37380 split.on('moved', splitterMoved);
37381 </code></pre>
37382  * @constructor
37383  * Create a new SplitBar
37384  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37385  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37386  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37387  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37388                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37389                         position of the SplitBar).
37390  */
37391 Roo.bootstrap.SplitBar = function(cfg){
37392     
37393     /** @private */
37394     
37395     //{
37396     //  dragElement : elm
37397     //  resizingElement: el,
37398         // optional..
37399     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37400     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37401         // existingProxy ???
37402     //}
37403     
37404     this.el = Roo.get(cfg.dragElement, true);
37405     this.el.dom.unselectable = "on";
37406     /** @private */
37407     this.resizingEl = Roo.get(cfg.resizingElement, true);
37408
37409     /**
37410      * @private
37411      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37412      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37413      * @type Number
37414      */
37415     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37416     
37417     /**
37418      * The minimum size of the resizing element. (Defaults to 0)
37419      * @type Number
37420      */
37421     this.minSize = 0;
37422     
37423     /**
37424      * The maximum size of the resizing element. (Defaults to 2000)
37425      * @type Number
37426      */
37427     this.maxSize = 2000;
37428     
37429     /**
37430      * Whether to animate the transition to the new size
37431      * @type Boolean
37432      */
37433     this.animate = false;
37434     
37435     /**
37436      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37437      * @type Boolean
37438      */
37439     this.useShim = false;
37440     
37441     /** @private */
37442     this.shim = null;
37443     
37444     if(!cfg.existingProxy){
37445         /** @private */
37446         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37447     }else{
37448         this.proxy = Roo.get(cfg.existingProxy).dom;
37449     }
37450     /** @private */
37451     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37452     
37453     /** @private */
37454     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37455     
37456     /** @private */
37457     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37458     
37459     /** @private */
37460     this.dragSpecs = {};
37461     
37462     /**
37463      * @private The adapter to use to positon and resize elements
37464      */
37465     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37466     this.adapter.init(this);
37467     
37468     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37469         /** @private */
37470         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37471         this.el.addClass("roo-splitbar-h");
37472     }else{
37473         /** @private */
37474         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37475         this.el.addClass("roo-splitbar-v");
37476     }
37477     
37478     this.addEvents({
37479         /**
37480          * @event resize
37481          * Fires when the splitter is moved (alias for {@link #event-moved})
37482          * @param {Roo.bootstrap.SplitBar} this
37483          * @param {Number} newSize the new width or height
37484          */
37485         "resize" : true,
37486         /**
37487          * @event moved
37488          * Fires when the splitter is moved
37489          * @param {Roo.bootstrap.SplitBar} this
37490          * @param {Number} newSize the new width or height
37491          */
37492         "moved" : true,
37493         /**
37494          * @event beforeresize
37495          * Fires before the splitter is dragged
37496          * @param {Roo.bootstrap.SplitBar} this
37497          */
37498         "beforeresize" : true,
37499
37500         "beforeapply" : true
37501     });
37502
37503     Roo.util.Observable.call(this);
37504 };
37505
37506 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37507     onStartProxyDrag : function(x, y){
37508         this.fireEvent("beforeresize", this);
37509         if(!this.overlay){
37510             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37511             o.unselectable();
37512             o.enableDisplayMode("block");
37513             // all splitbars share the same overlay
37514             Roo.bootstrap.SplitBar.prototype.overlay = o;
37515         }
37516         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37517         this.overlay.show();
37518         Roo.get(this.proxy).setDisplayed("block");
37519         var size = this.adapter.getElementSize(this);
37520         this.activeMinSize = this.getMinimumSize();;
37521         this.activeMaxSize = this.getMaximumSize();;
37522         var c1 = size - this.activeMinSize;
37523         var c2 = Math.max(this.activeMaxSize - size, 0);
37524         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37525             this.dd.resetConstraints();
37526             this.dd.setXConstraint(
37527                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37528                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37529             );
37530             this.dd.setYConstraint(0, 0);
37531         }else{
37532             this.dd.resetConstraints();
37533             this.dd.setXConstraint(0, 0);
37534             this.dd.setYConstraint(
37535                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37536                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37537             );
37538          }
37539         this.dragSpecs.startSize = size;
37540         this.dragSpecs.startPoint = [x, y];
37541         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37542     },
37543     
37544     /** 
37545      * @private Called after the drag operation by the DDProxy
37546      */
37547     onEndProxyDrag : function(e){
37548         Roo.get(this.proxy).setDisplayed(false);
37549         var endPoint = Roo.lib.Event.getXY(e);
37550         if(this.overlay){
37551             this.overlay.hide();
37552         }
37553         var newSize;
37554         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37555             newSize = this.dragSpecs.startSize + 
37556                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37557                     endPoint[0] - this.dragSpecs.startPoint[0] :
37558                     this.dragSpecs.startPoint[0] - endPoint[0]
37559                 );
37560         }else{
37561             newSize = this.dragSpecs.startSize + 
37562                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37563                     endPoint[1] - this.dragSpecs.startPoint[1] :
37564                     this.dragSpecs.startPoint[1] - endPoint[1]
37565                 );
37566         }
37567         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37568         if(newSize != this.dragSpecs.startSize){
37569             if(this.fireEvent('beforeapply', this, newSize) !== false){
37570                 this.adapter.setElementSize(this, newSize);
37571                 this.fireEvent("moved", this, newSize);
37572                 this.fireEvent("resize", this, newSize);
37573             }
37574         }
37575     },
37576     
37577     /**
37578      * Get the adapter this SplitBar uses
37579      * @return The adapter object
37580      */
37581     getAdapter : function(){
37582         return this.adapter;
37583     },
37584     
37585     /**
37586      * Set the adapter this SplitBar uses
37587      * @param {Object} adapter A SplitBar adapter object
37588      */
37589     setAdapter : function(adapter){
37590         this.adapter = adapter;
37591         this.adapter.init(this);
37592     },
37593     
37594     /**
37595      * Gets the minimum size for the resizing element
37596      * @return {Number} The minimum size
37597      */
37598     getMinimumSize : function(){
37599         return this.minSize;
37600     },
37601     
37602     /**
37603      * Sets the minimum size for the resizing element
37604      * @param {Number} minSize The minimum size
37605      */
37606     setMinimumSize : function(minSize){
37607         this.minSize = minSize;
37608     },
37609     
37610     /**
37611      * Gets the maximum size for the resizing element
37612      * @return {Number} The maximum size
37613      */
37614     getMaximumSize : function(){
37615         return this.maxSize;
37616     },
37617     
37618     /**
37619      * Sets the maximum size for the resizing element
37620      * @param {Number} maxSize The maximum size
37621      */
37622     setMaximumSize : function(maxSize){
37623         this.maxSize = maxSize;
37624     },
37625     
37626     /**
37627      * Sets the initialize size for the resizing element
37628      * @param {Number} size The initial size
37629      */
37630     setCurrentSize : function(size){
37631         var oldAnimate = this.animate;
37632         this.animate = false;
37633         this.adapter.setElementSize(this, size);
37634         this.animate = oldAnimate;
37635     },
37636     
37637     /**
37638      * Destroy this splitbar. 
37639      * @param {Boolean} removeEl True to remove the element
37640      */
37641     destroy : function(removeEl){
37642         if(this.shim){
37643             this.shim.remove();
37644         }
37645         this.dd.unreg();
37646         this.proxy.parentNode.removeChild(this.proxy);
37647         if(removeEl){
37648             this.el.remove();
37649         }
37650     }
37651 });
37652
37653 /**
37654  * @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.
37655  */
37656 Roo.bootstrap.SplitBar.createProxy = function(dir){
37657     var proxy = new Roo.Element(document.createElement("div"));
37658     proxy.unselectable();
37659     var cls = 'roo-splitbar-proxy';
37660     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37661     document.body.appendChild(proxy.dom);
37662     return proxy.dom;
37663 };
37664
37665 /** 
37666  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37667  * Default Adapter. It assumes the splitter and resizing element are not positioned
37668  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37669  */
37670 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37671 };
37672
37673 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37674     // do nothing for now
37675     init : function(s){
37676     
37677     },
37678     /**
37679      * Called before drag operations to get the current size of the resizing element. 
37680      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37681      */
37682      getElementSize : function(s){
37683         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37684             return s.resizingEl.getWidth();
37685         }else{
37686             return s.resizingEl.getHeight();
37687         }
37688     },
37689     
37690     /**
37691      * Called after drag operations to set the size of the resizing element.
37692      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37693      * @param {Number} newSize The new size to set
37694      * @param {Function} onComplete A function to be invoked when resizing is complete
37695      */
37696     setElementSize : function(s, newSize, onComplete){
37697         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37698             if(!s.animate){
37699                 s.resizingEl.setWidth(newSize);
37700                 if(onComplete){
37701                     onComplete(s, newSize);
37702                 }
37703             }else{
37704                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37705             }
37706         }else{
37707             
37708             if(!s.animate){
37709                 s.resizingEl.setHeight(newSize);
37710                 if(onComplete){
37711                     onComplete(s, newSize);
37712                 }
37713             }else{
37714                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37715             }
37716         }
37717     }
37718 };
37719
37720 /** 
37721  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37722  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37723  * Adapter that  moves the splitter element to align with the resized sizing element. 
37724  * Used with an absolute positioned SplitBar.
37725  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37726  * document.body, make sure you assign an id to the body element.
37727  */
37728 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37729     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37730     this.container = Roo.get(container);
37731 };
37732
37733 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37734     init : function(s){
37735         this.basic.init(s);
37736     },
37737     
37738     getElementSize : function(s){
37739         return this.basic.getElementSize(s);
37740     },
37741     
37742     setElementSize : function(s, newSize, onComplete){
37743         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37744     },
37745     
37746     moveSplitter : function(s){
37747         var yes = Roo.bootstrap.SplitBar;
37748         switch(s.placement){
37749             case yes.LEFT:
37750                 s.el.setX(s.resizingEl.getRight());
37751                 break;
37752             case yes.RIGHT:
37753                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37754                 break;
37755             case yes.TOP:
37756                 s.el.setY(s.resizingEl.getBottom());
37757                 break;
37758             case yes.BOTTOM:
37759                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37760                 break;
37761         }
37762     }
37763 };
37764
37765 /**
37766  * Orientation constant - Create a vertical SplitBar
37767  * @static
37768  * @type Number
37769  */
37770 Roo.bootstrap.SplitBar.VERTICAL = 1;
37771
37772 /**
37773  * Orientation constant - Create a horizontal SplitBar
37774  * @static
37775  * @type Number
37776  */
37777 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37778
37779 /**
37780  * Placement constant - The resizing element is to the left of the splitter element
37781  * @static
37782  * @type Number
37783  */
37784 Roo.bootstrap.SplitBar.LEFT = 1;
37785
37786 /**
37787  * Placement constant - The resizing element is to the right of the splitter element
37788  * @static
37789  * @type Number
37790  */
37791 Roo.bootstrap.SplitBar.RIGHT = 2;
37792
37793 /**
37794  * Placement constant - The resizing element is positioned above the splitter element
37795  * @static
37796  * @type Number
37797  */
37798 Roo.bootstrap.SplitBar.TOP = 3;
37799
37800 /**
37801  * Placement constant - The resizing element is positioned under splitter element
37802  * @static
37803  * @type Number
37804  */
37805 Roo.bootstrap.SplitBar.BOTTOM = 4;
37806 /*
37807  * Based on:
37808  * Ext JS Library 1.1.1
37809  * Copyright(c) 2006-2007, Ext JS, LLC.
37810  *
37811  * Originally Released Under LGPL - original licence link has changed is not relivant.
37812  *
37813  * Fork - LGPL
37814  * <script type="text/javascript">
37815  */
37816
37817 /**
37818  * @class Roo.bootstrap.layout.Manager
37819  * @extends Roo.bootstrap.Component
37820  * @abstract
37821  * Base class for layout managers.
37822  */
37823 Roo.bootstrap.layout.Manager = function(config)
37824 {
37825     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37826
37827
37828
37829
37830
37831     /** false to disable window resize monitoring @type Boolean */
37832     this.monitorWindowResize = true;
37833     this.regions = {};
37834     this.addEvents({
37835         /**
37836          * @event layout
37837          * Fires when a layout is performed.
37838          * @param {Roo.LayoutManager} this
37839          */
37840         "layout" : true,
37841         /**
37842          * @event regionresized
37843          * Fires when the user resizes a region.
37844          * @param {Roo.LayoutRegion} region The resized region
37845          * @param {Number} newSize The new size (width for east/west, height for north/south)
37846          */
37847         "regionresized" : true,
37848         /**
37849          * @event regioncollapsed
37850          * Fires when a region is collapsed.
37851          * @param {Roo.LayoutRegion} region The collapsed region
37852          */
37853         "regioncollapsed" : true,
37854         /**
37855          * @event regionexpanded
37856          * Fires when a region is expanded.
37857          * @param {Roo.LayoutRegion} region The expanded region
37858          */
37859         "regionexpanded" : true
37860     });
37861     this.updating = false;
37862
37863     if (config.el) {
37864         this.el = Roo.get(config.el);
37865         this.initEvents();
37866     }
37867
37868 };
37869
37870 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37871
37872
37873     regions : null,
37874
37875     monitorWindowResize : true,
37876
37877
37878     updating : false,
37879
37880
37881     onRender : function(ct, position)
37882     {
37883         if(!this.el){
37884             this.el = Roo.get(ct);
37885             this.initEvents();
37886         }
37887         //this.fireEvent('render',this);
37888     },
37889
37890
37891     initEvents: function()
37892     {
37893
37894
37895         // ie scrollbar fix
37896         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37897             document.body.scroll = "no";
37898         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37899             this.el.position('relative');
37900         }
37901         this.id = this.el.id;
37902         this.el.addClass("roo-layout-container");
37903         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37904         if(this.el.dom != document.body ) {
37905             this.el.on('resize', this.layout,this);
37906             this.el.on('show', this.layout,this);
37907         }
37908
37909     },
37910
37911     /**
37912      * Returns true if this layout is currently being updated
37913      * @return {Boolean}
37914      */
37915     isUpdating : function(){
37916         return this.updating;
37917     },
37918
37919     /**
37920      * Suspend the LayoutManager from doing auto-layouts while
37921      * making multiple add or remove calls
37922      */
37923     beginUpdate : function(){
37924         this.updating = true;
37925     },
37926
37927     /**
37928      * Restore auto-layouts and optionally disable the manager from performing a layout
37929      * @param {Boolean} noLayout true to disable a layout update
37930      */
37931     endUpdate : function(noLayout){
37932         this.updating = false;
37933         if(!noLayout){
37934             this.layout();
37935         }
37936     },
37937
37938     layout: function(){
37939         // abstract...
37940     },
37941
37942     onRegionResized : function(region, newSize){
37943         this.fireEvent("regionresized", region, newSize);
37944         this.layout();
37945     },
37946
37947     onRegionCollapsed : function(region){
37948         this.fireEvent("regioncollapsed", region);
37949     },
37950
37951     onRegionExpanded : function(region){
37952         this.fireEvent("regionexpanded", region);
37953     },
37954
37955     /**
37956      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37957      * performs box-model adjustments.
37958      * @return {Object} The size as an object {width: (the width), height: (the height)}
37959      */
37960     getViewSize : function()
37961     {
37962         var size;
37963         if(this.el.dom != document.body){
37964             size = this.el.getSize();
37965         }else{
37966             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37967         }
37968         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37969         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37970         return size;
37971     },
37972
37973     /**
37974      * Returns the Element this layout is bound to.
37975      * @return {Roo.Element}
37976      */
37977     getEl : function(){
37978         return this.el;
37979     },
37980
37981     /**
37982      * Returns the specified region.
37983      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37984      * @return {Roo.LayoutRegion}
37985      */
37986     getRegion : function(target){
37987         return this.regions[target.toLowerCase()];
37988     },
37989
37990     onWindowResize : function(){
37991         if(this.monitorWindowResize){
37992             this.layout();
37993         }
37994     }
37995 });
37996 /*
37997  * Based on:
37998  * Ext JS Library 1.1.1
37999  * Copyright(c) 2006-2007, Ext JS, LLC.
38000  *
38001  * Originally Released Under LGPL - original licence link has changed is not relivant.
38002  *
38003  * Fork - LGPL
38004  * <script type="text/javascript">
38005  */
38006 /**
38007  * @class Roo.bootstrap.layout.Border
38008  * @extends Roo.bootstrap.layout.Manager
38009  * @builder-top
38010  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38011  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38012  * please see: examples/bootstrap/nested.html<br><br>
38013  
38014 <b>The container the layout is rendered into can be either the body element or any other element.
38015 If it is not the body element, the container needs to either be an absolute positioned element,
38016 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38017 the container size if it is not the body element.</b>
38018
38019 * @constructor
38020 * Create a new Border
38021 * @param {Object} config Configuration options
38022  */
38023 Roo.bootstrap.layout.Border = function(config){
38024     config = config || {};
38025     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38026     
38027     
38028     
38029     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38030         if(config[region]){
38031             config[region].region = region;
38032             this.addRegion(config[region]);
38033         }
38034     },this);
38035     
38036 };
38037
38038 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38039
38040 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38041     
38042         /**
38043          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38044          */
38045         /**
38046          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38047          */
38048         /**
38049          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38050          */
38051         /**
38052          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38053          */
38054         /**
38055          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38056          */
38057         
38058         
38059         
38060         
38061     parent : false, // this might point to a 'nest' or a ???
38062     
38063     /**
38064      * Creates and adds a new region if it doesn't already exist.
38065      * @param {String} target The target region key (north, south, east, west or center).
38066      * @param {Object} config The regions config object
38067      * @return {BorderLayoutRegion} The new region
38068      */
38069     addRegion : function(config)
38070     {
38071         if(!this.regions[config.region]){
38072             var r = this.factory(config);
38073             this.bindRegion(r);
38074         }
38075         return this.regions[config.region];
38076     },
38077
38078     // private (kinda)
38079     bindRegion : function(r){
38080         this.regions[r.config.region] = r;
38081         
38082         r.on("visibilitychange",    this.layout, this);
38083         r.on("paneladded",          this.layout, this);
38084         r.on("panelremoved",        this.layout, this);
38085         r.on("invalidated",         this.layout, this);
38086         r.on("resized",             this.onRegionResized, this);
38087         r.on("collapsed",           this.onRegionCollapsed, this);
38088         r.on("expanded",            this.onRegionExpanded, this);
38089     },
38090
38091     /**
38092      * Performs a layout update.
38093      */
38094     layout : function()
38095     {
38096         if(this.updating) {
38097             return;
38098         }
38099         
38100         // render all the rebions if they have not been done alreayd?
38101         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38102             if(this.regions[region] && !this.regions[region].bodyEl){
38103                 this.regions[region].onRender(this.el)
38104             }
38105         },this);
38106         
38107         var size = this.getViewSize();
38108         var w = size.width;
38109         var h = size.height;
38110         var centerW = w;
38111         var centerH = h;
38112         var centerY = 0;
38113         var centerX = 0;
38114         //var x = 0, y = 0;
38115
38116         var rs = this.regions;
38117         var north = rs["north"];
38118         var south = rs["south"]; 
38119         var west = rs["west"];
38120         var east = rs["east"];
38121         var center = rs["center"];
38122         //if(this.hideOnLayout){ // not supported anymore
38123             //c.el.setStyle("display", "none");
38124         //}
38125         if(north && north.isVisible()){
38126             var b = north.getBox();
38127             var m = north.getMargins();
38128             b.width = w - (m.left+m.right);
38129             b.x = m.left;
38130             b.y = m.top;
38131             centerY = b.height + b.y + m.bottom;
38132             centerH -= centerY;
38133             north.updateBox(this.safeBox(b));
38134         }
38135         if(south && south.isVisible()){
38136             var b = south.getBox();
38137             var m = south.getMargins();
38138             b.width = w - (m.left+m.right);
38139             b.x = m.left;
38140             var totalHeight = (b.height + m.top + m.bottom);
38141             b.y = h - totalHeight + m.top;
38142             centerH -= totalHeight;
38143             south.updateBox(this.safeBox(b));
38144         }
38145         if(west && west.isVisible()){
38146             var b = west.getBox();
38147             var m = west.getMargins();
38148             b.height = centerH - (m.top+m.bottom);
38149             b.x = m.left;
38150             b.y = centerY + m.top;
38151             var totalWidth = (b.width + m.left + m.right);
38152             centerX += totalWidth;
38153             centerW -= totalWidth;
38154             west.updateBox(this.safeBox(b));
38155         }
38156         if(east && east.isVisible()){
38157             var b = east.getBox();
38158             var m = east.getMargins();
38159             b.height = centerH - (m.top+m.bottom);
38160             var totalWidth = (b.width + m.left + m.right);
38161             b.x = w - totalWidth + m.left;
38162             b.y = centerY + m.top;
38163             centerW -= totalWidth;
38164             east.updateBox(this.safeBox(b));
38165         }
38166         if(center){
38167             var m = center.getMargins();
38168             var centerBox = {
38169                 x: centerX + m.left,
38170                 y: centerY + m.top,
38171                 width: centerW - (m.left+m.right),
38172                 height: centerH - (m.top+m.bottom)
38173             };
38174             //if(this.hideOnLayout){
38175                 //center.el.setStyle("display", "block");
38176             //}
38177             center.updateBox(this.safeBox(centerBox));
38178         }
38179         this.el.repaint();
38180         this.fireEvent("layout", this);
38181     },
38182
38183     // private
38184     safeBox : function(box){
38185         box.width = Math.max(0, box.width);
38186         box.height = Math.max(0, box.height);
38187         return box;
38188     },
38189
38190     /**
38191      * Adds a ContentPanel (or subclass) to this layout.
38192      * @param {String} target The target region key (north, south, east, west or center).
38193      * @param {Roo.ContentPanel} panel The panel to add
38194      * @return {Roo.ContentPanel} The added panel
38195      */
38196     add : function(target, panel){
38197          
38198         target = target.toLowerCase();
38199         return this.regions[target].add(panel);
38200     },
38201
38202     /**
38203      * Remove a ContentPanel (or subclass) to this layout.
38204      * @param {String} target The target region key (north, south, east, west or center).
38205      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38206      * @return {Roo.ContentPanel} The removed panel
38207      */
38208     remove : function(target, panel){
38209         target = target.toLowerCase();
38210         return this.regions[target].remove(panel);
38211     },
38212
38213     /**
38214      * Searches all regions for a panel with the specified id
38215      * @param {String} panelId
38216      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38217      */
38218     findPanel : function(panelId){
38219         var rs = this.regions;
38220         for(var target in rs){
38221             if(typeof rs[target] != "function"){
38222                 var p = rs[target].getPanel(panelId);
38223                 if(p){
38224                     return p;
38225                 }
38226             }
38227         }
38228         return null;
38229     },
38230
38231     /**
38232      * Searches all regions for a panel with the specified id and activates (shows) it.
38233      * @param {String/ContentPanel} panelId The panels id or the panel itself
38234      * @return {Roo.ContentPanel} The shown panel or null
38235      */
38236     showPanel : function(panelId) {
38237       var rs = this.regions;
38238       for(var target in rs){
38239          var r = rs[target];
38240          if(typeof r != "function"){
38241             if(r.hasPanel(panelId)){
38242                return r.showPanel(panelId);
38243             }
38244          }
38245       }
38246       return null;
38247    },
38248
38249    /**
38250      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38251      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38252      */
38253    /*
38254     restoreState : function(provider){
38255         if(!provider){
38256             provider = Roo.state.Manager;
38257         }
38258         var sm = new Roo.LayoutStateManager();
38259         sm.init(this, provider);
38260     },
38261 */
38262  
38263  
38264     /**
38265      * Adds a xtype elements to the layout.
38266      * <pre><code>
38267
38268 layout.addxtype({
38269        xtype : 'ContentPanel',
38270        region: 'west',
38271        items: [ .... ]
38272    }
38273 );
38274
38275 layout.addxtype({
38276         xtype : 'NestedLayoutPanel',
38277         region: 'west',
38278         layout: {
38279            center: { },
38280            west: { }   
38281         },
38282         items : [ ... list of content panels or nested layout panels.. ]
38283    }
38284 );
38285 </code></pre>
38286      * @param {Object} cfg Xtype definition of item to add.
38287      */
38288     addxtype : function(cfg)
38289     {
38290         // basically accepts a pannel...
38291         // can accept a layout region..!?!?
38292         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38293         
38294         
38295         // theory?  children can only be panels??
38296         
38297         //if (!cfg.xtype.match(/Panel$/)) {
38298         //    return false;
38299         //}
38300         var ret = false;
38301         
38302         if (typeof(cfg.region) == 'undefined') {
38303             Roo.log("Failed to add Panel, region was not set");
38304             Roo.log(cfg);
38305             return false;
38306         }
38307         var region = cfg.region;
38308         delete cfg.region;
38309         
38310           
38311         var xitems = [];
38312         if (cfg.items) {
38313             xitems = cfg.items;
38314             delete cfg.items;
38315         }
38316         var nb = false;
38317         
38318         if ( region == 'center') {
38319             Roo.log("Center: " + cfg.title);
38320         }
38321         
38322         
38323         switch(cfg.xtype) 
38324         {
38325             case 'Content':  // ContentPanel (el, cfg)
38326             case 'Scroll':  // ContentPanel (el, cfg)
38327             case 'View': 
38328                 cfg.autoCreate = cfg.autoCreate || true;
38329                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38330                 //} else {
38331                 //    var el = this.el.createChild();
38332                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38333                 //}
38334                 
38335                 this.add(region, ret);
38336                 break;
38337             
38338             /*
38339             case 'TreePanel': // our new panel!
38340                 cfg.el = this.el.createChild();
38341                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38342                 this.add(region, ret);
38343                 break;
38344             */
38345             
38346             case 'Nest': 
38347                 // create a new Layout (which is  a Border Layout...
38348                 
38349                 var clayout = cfg.layout;
38350                 clayout.el  = this.el.createChild();
38351                 clayout.items   = clayout.items  || [];
38352                 
38353                 delete cfg.layout;
38354                 
38355                 // replace this exitems with the clayout ones..
38356                 xitems = clayout.items;
38357                  
38358                 // force background off if it's in center...
38359                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38360                     cfg.background = false;
38361                 }
38362                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38363                 
38364                 
38365                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38366                 //console.log('adding nested layout panel '  + cfg.toSource());
38367                 this.add(region, ret);
38368                 nb = {}; /// find first...
38369                 break;
38370             
38371             case 'Grid':
38372                 
38373                 // needs grid and region
38374                 
38375                 //var el = this.getRegion(region).el.createChild();
38376                 /*
38377                  *var el = this.el.createChild();
38378                 // create the grid first...
38379                 cfg.grid.container = el;
38380                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38381                 */
38382                 
38383                 if (region == 'center' && this.active ) {
38384                     cfg.background = false;
38385                 }
38386                 
38387                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38388                 
38389                 this.add(region, ret);
38390                 /*
38391                 if (cfg.background) {
38392                     // render grid on panel activation (if panel background)
38393                     ret.on('activate', function(gp) {
38394                         if (!gp.grid.rendered) {
38395                     //        gp.grid.render(el);
38396                         }
38397                     });
38398                 } else {
38399                   //  cfg.grid.render(el);
38400                 }
38401                 */
38402                 break;
38403            
38404            
38405             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38406                 // it was the old xcomponent building that caused this before.
38407                 // espeically if border is the top element in the tree.
38408                 ret = this;
38409                 break; 
38410                 
38411                     
38412                 
38413                 
38414                 
38415             default:
38416                 /*
38417                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38418                     
38419                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38420                     this.add(region, ret);
38421                 } else {
38422                 */
38423                     Roo.log(cfg);
38424                     throw "Can not add '" + cfg.xtype + "' to Border";
38425                     return null;
38426              
38427                                 
38428              
38429         }
38430         this.beginUpdate();
38431         // add children..
38432         var region = '';
38433         var abn = {};
38434         Roo.each(xitems, function(i)  {
38435             region = nb && i.region ? i.region : false;
38436             
38437             var add = ret.addxtype(i);
38438            
38439             if (region) {
38440                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38441                 if (!i.background) {
38442                     abn[region] = nb[region] ;
38443                 }
38444             }
38445             
38446         });
38447         this.endUpdate();
38448
38449         // make the last non-background panel active..
38450         //if (nb) { Roo.log(abn); }
38451         if (nb) {
38452             
38453             for(var r in abn) {
38454                 region = this.getRegion(r);
38455                 if (region) {
38456                     // tried using nb[r], but it does not work..
38457                      
38458                     region.showPanel(abn[r]);
38459                    
38460                 }
38461             }
38462         }
38463         return ret;
38464         
38465     },
38466     
38467     
38468 // private
38469     factory : function(cfg)
38470     {
38471         
38472         var validRegions = Roo.bootstrap.layout.Border.regions;
38473
38474         var target = cfg.region;
38475         cfg.mgr = this;
38476         
38477         var r = Roo.bootstrap.layout;
38478         Roo.log(target);
38479         switch(target){
38480             case "north":
38481                 return new r.North(cfg);
38482             case "south":
38483                 return new r.South(cfg);
38484             case "east":
38485                 return new r.East(cfg);
38486             case "west":
38487                 return new r.West(cfg);
38488             case "center":
38489                 return new r.Center(cfg);
38490         }
38491         throw 'Layout region "'+target+'" not supported.';
38492     }
38493     
38494     
38495 });
38496  /*
38497  * Based on:
38498  * Ext JS Library 1.1.1
38499  * Copyright(c) 2006-2007, Ext JS, LLC.
38500  *
38501  * Originally Released Under LGPL - original licence link has changed is not relivant.
38502  *
38503  * Fork - LGPL
38504  * <script type="text/javascript">
38505  */
38506  
38507 /**
38508  * @class Roo.bootstrap.layout.Basic
38509  * @extends Roo.util.Observable
38510  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38511  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38512  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38513  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38514  * @cfg {string}   region  the region that it inhabits..
38515  * @cfg {bool}   skipConfig skip config?
38516  * 
38517
38518  */
38519 Roo.bootstrap.layout.Basic = function(config){
38520     
38521     this.mgr = config.mgr;
38522     
38523     this.position = config.region;
38524     
38525     var skipConfig = config.skipConfig;
38526     
38527     this.events = {
38528         /**
38529          * @scope Roo.BasicLayoutRegion
38530          */
38531         
38532         /**
38533          * @event beforeremove
38534          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38535          * @param {Roo.LayoutRegion} this
38536          * @param {Roo.ContentPanel} panel The panel
38537          * @param {Object} e The cancel event object
38538          */
38539         "beforeremove" : true,
38540         /**
38541          * @event invalidated
38542          * Fires when the layout for this region is changed.
38543          * @param {Roo.LayoutRegion} this
38544          */
38545         "invalidated" : true,
38546         /**
38547          * @event visibilitychange
38548          * Fires when this region is shown or hidden 
38549          * @param {Roo.LayoutRegion} this
38550          * @param {Boolean} visibility true or false
38551          */
38552         "visibilitychange" : true,
38553         /**
38554          * @event paneladded
38555          * Fires when a panel is added. 
38556          * @param {Roo.LayoutRegion} this
38557          * @param {Roo.ContentPanel} panel The panel
38558          */
38559         "paneladded" : true,
38560         /**
38561          * @event panelremoved
38562          * Fires when a panel is removed. 
38563          * @param {Roo.LayoutRegion} this
38564          * @param {Roo.ContentPanel} panel The panel
38565          */
38566         "panelremoved" : true,
38567         /**
38568          * @event beforecollapse
38569          * Fires when this region before collapse.
38570          * @param {Roo.LayoutRegion} this
38571          */
38572         "beforecollapse" : true,
38573         /**
38574          * @event collapsed
38575          * Fires when this region is collapsed.
38576          * @param {Roo.LayoutRegion} this
38577          */
38578         "collapsed" : true,
38579         /**
38580          * @event expanded
38581          * Fires when this region is expanded.
38582          * @param {Roo.LayoutRegion} this
38583          */
38584         "expanded" : true,
38585         /**
38586          * @event slideshow
38587          * Fires when this region is slid into view.
38588          * @param {Roo.LayoutRegion} this
38589          */
38590         "slideshow" : true,
38591         /**
38592          * @event slidehide
38593          * Fires when this region slides out of view. 
38594          * @param {Roo.LayoutRegion} this
38595          */
38596         "slidehide" : true,
38597         /**
38598          * @event panelactivated
38599          * Fires when a panel is activated. 
38600          * @param {Roo.LayoutRegion} this
38601          * @param {Roo.ContentPanel} panel The activated panel
38602          */
38603         "panelactivated" : true,
38604         /**
38605          * @event resized
38606          * Fires when the user resizes this region. 
38607          * @param {Roo.LayoutRegion} this
38608          * @param {Number} newSize The new size (width for east/west, height for north/south)
38609          */
38610         "resized" : true
38611     };
38612     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38613     this.panels = new Roo.util.MixedCollection();
38614     this.panels.getKey = this.getPanelId.createDelegate(this);
38615     this.box = null;
38616     this.activePanel = null;
38617     // ensure listeners are added...
38618     
38619     if (config.listeners || config.events) {
38620         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38621             listeners : config.listeners || {},
38622             events : config.events || {}
38623         });
38624     }
38625     
38626     if(skipConfig !== true){
38627         this.applyConfig(config);
38628     }
38629 };
38630
38631 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38632 {
38633     getPanelId : function(p){
38634         return p.getId();
38635     },
38636     
38637     applyConfig : function(config){
38638         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38639         this.config = config;
38640         
38641     },
38642     
38643     /**
38644      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38645      * the width, for horizontal (north, south) the height.
38646      * @param {Number} newSize The new width or height
38647      */
38648     resizeTo : function(newSize){
38649         var el = this.el ? this.el :
38650                  (this.activePanel ? this.activePanel.getEl() : null);
38651         if(el){
38652             switch(this.position){
38653                 case "east":
38654                 case "west":
38655                     el.setWidth(newSize);
38656                     this.fireEvent("resized", this, newSize);
38657                 break;
38658                 case "north":
38659                 case "south":
38660                     el.setHeight(newSize);
38661                     this.fireEvent("resized", this, newSize);
38662                 break;                
38663             }
38664         }
38665     },
38666     
38667     getBox : function(){
38668         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38669     },
38670     
38671     getMargins : function(){
38672         return this.margins;
38673     },
38674     
38675     updateBox : function(box){
38676         this.box = box;
38677         var el = this.activePanel.getEl();
38678         el.dom.style.left = box.x + "px";
38679         el.dom.style.top = box.y + "px";
38680         this.activePanel.setSize(box.width, box.height);
38681     },
38682     
38683     /**
38684      * Returns the container element for this region.
38685      * @return {Roo.Element}
38686      */
38687     getEl : function(){
38688         return this.activePanel;
38689     },
38690     
38691     /**
38692      * Returns true if this region is currently visible.
38693      * @return {Boolean}
38694      */
38695     isVisible : function(){
38696         return this.activePanel ? true : false;
38697     },
38698     
38699     setActivePanel : function(panel){
38700         panel = this.getPanel(panel);
38701         if(this.activePanel && this.activePanel != panel){
38702             this.activePanel.setActiveState(false);
38703             this.activePanel.getEl().setLeftTop(-10000,-10000);
38704         }
38705         this.activePanel = panel;
38706         panel.setActiveState(true);
38707         if(this.box){
38708             panel.setSize(this.box.width, this.box.height);
38709         }
38710         this.fireEvent("panelactivated", this, panel);
38711         this.fireEvent("invalidated");
38712     },
38713     
38714     /**
38715      * Show the specified panel.
38716      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38717      * @return {Roo.ContentPanel} The shown panel or null
38718      */
38719     showPanel : function(panel){
38720         panel = this.getPanel(panel);
38721         if(panel){
38722             this.setActivePanel(panel);
38723         }
38724         return panel;
38725     },
38726     
38727     /**
38728      * Get the active panel for this region.
38729      * @return {Roo.ContentPanel} The active panel or null
38730      */
38731     getActivePanel : function(){
38732         return this.activePanel;
38733     },
38734     
38735     /**
38736      * Add the passed ContentPanel(s)
38737      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38738      * @return {Roo.ContentPanel} The panel added (if only one was added)
38739      */
38740     add : function(panel){
38741         if(arguments.length > 1){
38742             for(var i = 0, len = arguments.length; i < len; i++) {
38743                 this.add(arguments[i]);
38744             }
38745             return null;
38746         }
38747         if(this.hasPanel(panel)){
38748             this.showPanel(panel);
38749             return panel;
38750         }
38751         var el = panel.getEl();
38752         if(el.dom.parentNode != this.mgr.el.dom){
38753             this.mgr.el.dom.appendChild(el.dom);
38754         }
38755         if(panel.setRegion){
38756             panel.setRegion(this);
38757         }
38758         this.panels.add(panel);
38759         el.setStyle("position", "absolute");
38760         if(!panel.background){
38761             this.setActivePanel(panel);
38762             if(this.config.initialSize && this.panels.getCount()==1){
38763                 this.resizeTo(this.config.initialSize);
38764             }
38765         }
38766         this.fireEvent("paneladded", this, panel);
38767         return panel;
38768     },
38769     
38770     /**
38771      * Returns true if the panel is in this region.
38772      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38773      * @return {Boolean}
38774      */
38775     hasPanel : function(panel){
38776         if(typeof panel == "object"){ // must be panel obj
38777             panel = panel.getId();
38778         }
38779         return this.getPanel(panel) ? true : false;
38780     },
38781     
38782     /**
38783      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38784      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38785      * @param {Boolean} preservePanel Overrides the config preservePanel option
38786      * @return {Roo.ContentPanel} The panel that was removed
38787      */
38788     remove : function(panel, preservePanel){
38789         panel = this.getPanel(panel);
38790         if(!panel){
38791             return null;
38792         }
38793         var e = {};
38794         this.fireEvent("beforeremove", this, panel, e);
38795         if(e.cancel === true){
38796             return null;
38797         }
38798         var panelId = panel.getId();
38799         this.panels.removeKey(panelId);
38800         return panel;
38801     },
38802     
38803     /**
38804      * Returns the panel specified or null if it's not in this region.
38805      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38806      * @return {Roo.ContentPanel}
38807      */
38808     getPanel : function(id){
38809         if(typeof id == "object"){ // must be panel obj
38810             return id;
38811         }
38812         return this.panels.get(id);
38813     },
38814     
38815     /**
38816      * Returns this regions position (north/south/east/west/center).
38817      * @return {String} 
38818      */
38819     getPosition: function(){
38820         return this.position;    
38821     }
38822 });/*
38823  * Based on:
38824  * Ext JS Library 1.1.1
38825  * Copyright(c) 2006-2007, Ext JS, LLC.
38826  *
38827  * Originally Released Under LGPL - original licence link has changed is not relivant.
38828  *
38829  * Fork - LGPL
38830  * <script type="text/javascript">
38831  */
38832  
38833 /**
38834  * @class Roo.bootstrap.layout.Region
38835  * @extends Roo.bootstrap.layout.Basic
38836  * This class represents a region in a layout manager.
38837  
38838  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38839  * @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})
38840  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38841  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38842  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38843  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38844  * @cfg {String}    title           The title for the region (overrides panel titles)
38845  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38846  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38847  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38848  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38849  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38850  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38851  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38852  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38853  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38854  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38855
38856  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38857  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38858  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38859  * @cfg {Number}    width           For East/West panels
38860  * @cfg {Number}    height          For North/South panels
38861  * @cfg {Boolean}   split           To show the splitter
38862  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38863  * 
38864  * @cfg {string}   cls             Extra CSS classes to add to region
38865  * 
38866  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38867  * @cfg {string}   region  the region that it inhabits..
38868  *
38869
38870  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38871  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38872
38873  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38874  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38875  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38876  */
38877 Roo.bootstrap.layout.Region = function(config)
38878 {
38879     this.applyConfig(config);
38880
38881     var mgr = config.mgr;
38882     var pos = config.region;
38883     config.skipConfig = true;
38884     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38885     
38886     if (mgr.el) {
38887         this.onRender(mgr.el);   
38888     }
38889      
38890     this.visible = true;
38891     this.collapsed = false;
38892     this.unrendered_panels = [];
38893 };
38894
38895 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38896
38897     position: '', // set by wrapper (eg. north/south etc..)
38898     unrendered_panels : null,  // unrendered panels.
38899     
38900     tabPosition : false,
38901     
38902     mgr: false, // points to 'Border'
38903     
38904     
38905     createBody : function(){
38906         /** This region's body element 
38907         * @type Roo.Element */
38908         this.bodyEl = this.el.createChild({
38909                 tag: "div",
38910                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38911         });
38912     },
38913
38914     onRender: function(ctr, pos)
38915     {
38916         var dh = Roo.DomHelper;
38917         /** This region's container element 
38918         * @type Roo.Element */
38919         this.el = dh.append(ctr.dom, {
38920                 tag: "div",
38921                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38922             }, true);
38923         /** This region's title element 
38924         * @type Roo.Element */
38925     
38926         this.titleEl = dh.append(this.el.dom,  {
38927                 tag: "div",
38928                 unselectable: "on",
38929                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38930                 children:[
38931                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38932                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38933                 ]
38934             }, true);
38935         
38936         this.titleEl.enableDisplayMode();
38937         /** This region's title text element 
38938         * @type HTMLElement */
38939         this.titleTextEl = this.titleEl.dom.firstChild;
38940         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38941         /*
38942         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38943         this.closeBtn.enableDisplayMode();
38944         this.closeBtn.on("click", this.closeClicked, this);
38945         this.closeBtn.hide();
38946     */
38947         this.createBody(this.config);
38948         if(this.config.hideWhenEmpty){
38949             this.hide();
38950             this.on("paneladded", this.validateVisibility, this);
38951             this.on("panelremoved", this.validateVisibility, this);
38952         }
38953         if(this.autoScroll){
38954             this.bodyEl.setStyle("overflow", "auto");
38955         }else{
38956             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38957         }
38958         //if(c.titlebar !== false){
38959             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38960                 this.titleEl.hide();
38961             }else{
38962                 this.titleEl.show();
38963                 if(this.config.title){
38964                     this.titleTextEl.innerHTML = this.config.title;
38965                 }
38966             }
38967         //}
38968         if(this.config.collapsed){
38969             this.collapse(true);
38970         }
38971         if(this.config.hidden){
38972             this.hide();
38973         }
38974         
38975         if (this.unrendered_panels && this.unrendered_panels.length) {
38976             for (var i =0;i< this.unrendered_panels.length; i++) {
38977                 this.add(this.unrendered_panels[i]);
38978             }
38979             this.unrendered_panels = null;
38980             
38981         }
38982         
38983     },
38984     
38985     applyConfig : function(c)
38986     {
38987         /*
38988          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38989             var dh = Roo.DomHelper;
38990             if(c.titlebar !== false){
38991                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38992                 this.collapseBtn.on("click", this.collapse, this);
38993                 this.collapseBtn.enableDisplayMode();
38994                 /*
38995                 if(c.showPin === true || this.showPin){
38996                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38997                     this.stickBtn.enableDisplayMode();
38998                     this.stickBtn.on("click", this.expand, this);
38999                     this.stickBtn.hide();
39000                 }
39001                 
39002             }
39003             */
39004             /** This region's collapsed element
39005             * @type Roo.Element */
39006             /*
39007              *
39008             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39009                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39010             ]}, true);
39011             
39012             if(c.floatable !== false){
39013                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39014                this.collapsedEl.on("click", this.collapseClick, this);
39015             }
39016
39017             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39018                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39019                    id: "message", unselectable: "on", style:{"float":"left"}});
39020                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39021              }
39022             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39023             this.expandBtn.on("click", this.expand, this);
39024             
39025         }
39026         
39027         if(this.collapseBtn){
39028             this.collapseBtn.setVisible(c.collapsible == true);
39029         }
39030         
39031         this.cmargins = c.cmargins || this.cmargins ||
39032                          (this.position == "west" || this.position == "east" ?
39033                              {top: 0, left: 2, right:2, bottom: 0} :
39034                              {top: 2, left: 0, right:0, bottom: 2});
39035         */
39036         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39037         
39038         
39039         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39040         
39041         this.autoScroll = c.autoScroll || false;
39042         
39043         
39044        
39045         
39046         this.duration = c.duration || .30;
39047         this.slideDuration = c.slideDuration || .45;
39048         this.config = c;
39049        
39050     },
39051     /**
39052      * Returns true if this region is currently visible.
39053      * @return {Boolean}
39054      */
39055     isVisible : function(){
39056         return this.visible;
39057     },
39058
39059     /**
39060      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39061      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39062      */
39063     //setCollapsedTitle : function(title){
39064     //    title = title || "&#160;";
39065      //   if(this.collapsedTitleTextEl){
39066       //      this.collapsedTitleTextEl.innerHTML = title;
39067        // }
39068     //},
39069
39070     getBox : function(){
39071         var b;
39072       //  if(!this.collapsed){
39073             b = this.el.getBox(false, true);
39074        // }else{
39075           //  b = this.collapsedEl.getBox(false, true);
39076         //}
39077         return b;
39078     },
39079
39080     getMargins : function(){
39081         return this.margins;
39082         //return this.collapsed ? this.cmargins : this.margins;
39083     },
39084 /*
39085     highlight : function(){
39086         this.el.addClass("x-layout-panel-dragover");
39087     },
39088
39089     unhighlight : function(){
39090         this.el.removeClass("x-layout-panel-dragover");
39091     },
39092 */
39093     updateBox : function(box)
39094     {
39095         if (!this.bodyEl) {
39096             return; // not rendered yet..
39097         }
39098         
39099         this.box = box;
39100         if(!this.collapsed){
39101             this.el.dom.style.left = box.x + "px";
39102             this.el.dom.style.top = box.y + "px";
39103             this.updateBody(box.width, box.height);
39104         }else{
39105             this.collapsedEl.dom.style.left = box.x + "px";
39106             this.collapsedEl.dom.style.top = box.y + "px";
39107             this.collapsedEl.setSize(box.width, box.height);
39108         }
39109         if(this.tabs){
39110             this.tabs.autoSizeTabs();
39111         }
39112     },
39113
39114     updateBody : function(w, h)
39115     {
39116         if(w !== null){
39117             this.el.setWidth(w);
39118             w -= this.el.getBorderWidth("rl");
39119             if(this.config.adjustments){
39120                 w += this.config.adjustments[0];
39121             }
39122         }
39123         if(h !== null && h > 0){
39124             this.el.setHeight(h);
39125             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39126             h -= this.el.getBorderWidth("tb");
39127             if(this.config.adjustments){
39128                 h += this.config.adjustments[1];
39129             }
39130             this.bodyEl.setHeight(h);
39131             if(this.tabs){
39132                 h = this.tabs.syncHeight(h);
39133             }
39134         }
39135         if(this.panelSize){
39136             w = w !== null ? w : this.panelSize.width;
39137             h = h !== null ? h : this.panelSize.height;
39138         }
39139         if(this.activePanel){
39140             var el = this.activePanel.getEl();
39141             w = w !== null ? w : el.getWidth();
39142             h = h !== null ? h : el.getHeight();
39143             this.panelSize = {width: w, height: h};
39144             this.activePanel.setSize(w, h);
39145         }
39146         if(Roo.isIE && this.tabs){
39147             this.tabs.el.repaint();
39148         }
39149     },
39150
39151     /**
39152      * Returns the container element for this region.
39153      * @return {Roo.Element}
39154      */
39155     getEl : function(){
39156         return this.el;
39157     },
39158
39159     /**
39160      * Hides this region.
39161      */
39162     hide : function(){
39163         //if(!this.collapsed){
39164             this.el.dom.style.left = "-2000px";
39165             this.el.hide();
39166         //}else{
39167          //   this.collapsedEl.dom.style.left = "-2000px";
39168          //   this.collapsedEl.hide();
39169        // }
39170         this.visible = false;
39171         this.fireEvent("visibilitychange", this, false);
39172     },
39173
39174     /**
39175      * Shows this region if it was previously hidden.
39176      */
39177     show : function(){
39178         //if(!this.collapsed){
39179             this.el.show();
39180         //}else{
39181         //    this.collapsedEl.show();
39182        // }
39183         this.visible = true;
39184         this.fireEvent("visibilitychange", this, true);
39185     },
39186 /*
39187     closeClicked : function(){
39188         if(this.activePanel){
39189             this.remove(this.activePanel);
39190         }
39191     },
39192
39193     collapseClick : function(e){
39194         if(this.isSlid){
39195            e.stopPropagation();
39196            this.slideIn();
39197         }else{
39198            e.stopPropagation();
39199            this.slideOut();
39200         }
39201     },
39202 */
39203     /**
39204      * Collapses this region.
39205      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39206      */
39207     /*
39208     collapse : function(skipAnim, skipCheck = false){
39209         if(this.collapsed) {
39210             return;
39211         }
39212         
39213         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39214             
39215             this.collapsed = true;
39216             if(this.split){
39217                 this.split.el.hide();
39218             }
39219             if(this.config.animate && skipAnim !== true){
39220                 this.fireEvent("invalidated", this);
39221                 this.animateCollapse();
39222             }else{
39223                 this.el.setLocation(-20000,-20000);
39224                 this.el.hide();
39225                 this.collapsedEl.show();
39226                 this.fireEvent("collapsed", this);
39227                 this.fireEvent("invalidated", this);
39228             }
39229         }
39230         
39231     },
39232 */
39233     animateCollapse : function(){
39234         // overridden
39235     },
39236
39237     /**
39238      * Expands this region if it was previously collapsed.
39239      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39240      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39241      */
39242     /*
39243     expand : function(e, skipAnim){
39244         if(e) {
39245             e.stopPropagation();
39246         }
39247         if(!this.collapsed || this.el.hasActiveFx()) {
39248             return;
39249         }
39250         if(this.isSlid){
39251             this.afterSlideIn();
39252             skipAnim = true;
39253         }
39254         this.collapsed = false;
39255         if(this.config.animate && skipAnim !== true){
39256             this.animateExpand();
39257         }else{
39258             this.el.show();
39259             if(this.split){
39260                 this.split.el.show();
39261             }
39262             this.collapsedEl.setLocation(-2000,-2000);
39263             this.collapsedEl.hide();
39264             this.fireEvent("invalidated", this);
39265             this.fireEvent("expanded", this);
39266         }
39267     },
39268 */
39269     animateExpand : function(){
39270         // overridden
39271     },
39272
39273     initTabs : function()
39274     {
39275         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39276         
39277         var ts = new Roo.bootstrap.panel.Tabs({
39278             el: this.bodyEl.dom,
39279             region : this,
39280             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39281             disableTooltips: this.config.disableTabTips,
39282             toolbar : this.config.toolbar
39283         });
39284         
39285         if(this.config.hideTabs){
39286             ts.stripWrap.setDisplayed(false);
39287         }
39288         this.tabs = ts;
39289         ts.resizeTabs = this.config.resizeTabs === true;
39290         ts.minTabWidth = this.config.minTabWidth || 40;
39291         ts.maxTabWidth = this.config.maxTabWidth || 250;
39292         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39293         ts.monitorResize = false;
39294         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39295         ts.bodyEl.addClass('roo-layout-tabs-body');
39296         this.panels.each(this.initPanelAsTab, this);
39297     },
39298
39299     initPanelAsTab : function(panel){
39300         var ti = this.tabs.addTab(
39301             panel.getEl().id,
39302             panel.getTitle(),
39303             null,
39304             this.config.closeOnTab && panel.isClosable(),
39305             panel.tpl
39306         );
39307         if(panel.tabTip !== undefined){
39308             ti.setTooltip(panel.tabTip);
39309         }
39310         ti.on("activate", function(){
39311               this.setActivePanel(panel);
39312         }, this);
39313         
39314         if(this.config.closeOnTab){
39315             ti.on("beforeclose", function(t, e){
39316                 e.cancel = true;
39317                 this.remove(panel);
39318             }, this);
39319         }
39320         
39321         panel.tabItem = ti;
39322         
39323         return ti;
39324     },
39325
39326     updatePanelTitle : function(panel, title)
39327     {
39328         if(this.activePanel == panel){
39329             this.updateTitle(title);
39330         }
39331         if(this.tabs){
39332             var ti = this.tabs.getTab(panel.getEl().id);
39333             ti.setText(title);
39334             if(panel.tabTip !== undefined){
39335                 ti.setTooltip(panel.tabTip);
39336             }
39337         }
39338     },
39339
39340     updateTitle : function(title){
39341         if(this.titleTextEl && !this.config.title){
39342             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39343         }
39344     },
39345
39346     setActivePanel : function(panel)
39347     {
39348         panel = this.getPanel(panel);
39349         if(this.activePanel && this.activePanel != panel){
39350             if(this.activePanel.setActiveState(false) === false){
39351                 return;
39352             }
39353         }
39354         this.activePanel = panel;
39355         panel.setActiveState(true);
39356         if(this.panelSize){
39357             panel.setSize(this.panelSize.width, this.panelSize.height);
39358         }
39359         if(this.closeBtn){
39360             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39361         }
39362         this.updateTitle(panel.getTitle());
39363         if(this.tabs){
39364             this.fireEvent("invalidated", this);
39365         }
39366         this.fireEvent("panelactivated", this, panel);
39367     },
39368
39369     /**
39370      * Shows the specified panel.
39371      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39372      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39373      */
39374     showPanel : function(panel)
39375     {
39376         panel = this.getPanel(panel);
39377         if(panel){
39378             if(this.tabs){
39379                 var tab = this.tabs.getTab(panel.getEl().id);
39380                 if(tab.isHidden()){
39381                     this.tabs.unhideTab(tab.id);
39382                 }
39383                 tab.activate();
39384             }else{
39385                 this.setActivePanel(panel);
39386             }
39387         }
39388         return panel;
39389     },
39390
39391     /**
39392      * Get the active panel for this region.
39393      * @return {Roo.ContentPanel} The active panel or null
39394      */
39395     getActivePanel : function(){
39396         return this.activePanel;
39397     },
39398
39399     validateVisibility : function(){
39400         if(this.panels.getCount() < 1){
39401             this.updateTitle("&#160;");
39402             this.closeBtn.hide();
39403             this.hide();
39404         }else{
39405             if(!this.isVisible()){
39406                 this.show();
39407             }
39408         }
39409     },
39410
39411     /**
39412      * Adds the passed ContentPanel(s) to this region.
39413      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39414      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39415      */
39416     add : function(panel)
39417     {
39418         if(arguments.length > 1){
39419             for(var i = 0, len = arguments.length; i < len; i++) {
39420                 this.add(arguments[i]);
39421             }
39422             return null;
39423         }
39424         
39425         // if we have not been rendered yet, then we can not really do much of this..
39426         if (!this.bodyEl) {
39427             this.unrendered_panels.push(panel);
39428             return panel;
39429         }
39430         
39431         
39432         
39433         
39434         if(this.hasPanel(panel)){
39435             this.showPanel(panel);
39436             return panel;
39437         }
39438         panel.setRegion(this);
39439         this.panels.add(panel);
39440        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39441             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39442             // and hide them... ???
39443             this.bodyEl.dom.appendChild(panel.getEl().dom);
39444             if(panel.background !== true){
39445                 this.setActivePanel(panel);
39446             }
39447             this.fireEvent("paneladded", this, panel);
39448             return panel;
39449         }
39450         */
39451         if(!this.tabs){
39452             this.initTabs();
39453         }else{
39454             this.initPanelAsTab(panel);
39455         }
39456         
39457         
39458         if(panel.background !== true){
39459             this.tabs.activate(panel.getEl().id);
39460         }
39461         this.fireEvent("paneladded", this, panel);
39462         return panel;
39463     },
39464
39465     /**
39466      * Hides the tab for the specified panel.
39467      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39468      */
39469     hidePanel : function(panel){
39470         if(this.tabs && (panel = this.getPanel(panel))){
39471             this.tabs.hideTab(panel.getEl().id);
39472         }
39473     },
39474
39475     /**
39476      * Unhides the tab for a previously hidden panel.
39477      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39478      */
39479     unhidePanel : function(panel){
39480         if(this.tabs && (panel = this.getPanel(panel))){
39481             this.tabs.unhideTab(panel.getEl().id);
39482         }
39483     },
39484
39485     clearPanels : function(){
39486         while(this.panels.getCount() > 0){
39487              this.remove(this.panels.first());
39488         }
39489     },
39490
39491     /**
39492      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39493      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39494      * @param {Boolean} preservePanel Overrides the config preservePanel option
39495      * @return {Roo.ContentPanel} The panel that was removed
39496      */
39497     remove : function(panel, preservePanel)
39498     {
39499         panel = this.getPanel(panel);
39500         if(!panel){
39501             return null;
39502         }
39503         var e = {};
39504         this.fireEvent("beforeremove", this, panel, e);
39505         if(e.cancel === true){
39506             return null;
39507         }
39508         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39509         var panelId = panel.getId();
39510         this.panels.removeKey(panelId);
39511         if(preservePanel){
39512             document.body.appendChild(panel.getEl().dom);
39513         }
39514         if(this.tabs){
39515             this.tabs.removeTab(panel.getEl().id);
39516         }else if (!preservePanel){
39517             this.bodyEl.dom.removeChild(panel.getEl().dom);
39518         }
39519         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39520             var p = this.panels.first();
39521             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39522             tempEl.appendChild(p.getEl().dom);
39523             this.bodyEl.update("");
39524             this.bodyEl.dom.appendChild(p.getEl().dom);
39525             tempEl = null;
39526             this.updateTitle(p.getTitle());
39527             this.tabs = null;
39528             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39529             this.setActivePanel(p);
39530         }
39531         panel.setRegion(null);
39532         if(this.activePanel == panel){
39533             this.activePanel = null;
39534         }
39535         if(this.config.autoDestroy !== false && preservePanel !== true){
39536             try{panel.destroy();}catch(e){}
39537         }
39538         this.fireEvent("panelremoved", this, panel);
39539         return panel;
39540     },
39541
39542     /**
39543      * Returns the TabPanel component used by this region
39544      * @return {Roo.TabPanel}
39545      */
39546     getTabs : function(){
39547         return this.tabs;
39548     },
39549
39550     createTool : function(parentEl, className){
39551         var btn = Roo.DomHelper.append(parentEl, {
39552             tag: "div",
39553             cls: "x-layout-tools-button",
39554             children: [ {
39555                 tag: "div",
39556                 cls: "roo-layout-tools-button-inner " + className,
39557                 html: "&#160;"
39558             }]
39559         }, true);
39560         btn.addClassOnOver("roo-layout-tools-button-over");
39561         return btn;
39562     }
39563 });/*
39564  * Based on:
39565  * Ext JS Library 1.1.1
39566  * Copyright(c) 2006-2007, Ext JS, LLC.
39567  *
39568  * Originally Released Under LGPL - original licence link has changed is not relivant.
39569  *
39570  * Fork - LGPL
39571  * <script type="text/javascript">
39572  */
39573  
39574
39575
39576 /**
39577  * @class Roo.SplitLayoutRegion
39578  * @extends Roo.LayoutRegion
39579  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39580  */
39581 Roo.bootstrap.layout.Split = function(config){
39582     this.cursor = config.cursor;
39583     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39584 };
39585
39586 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39587 {
39588     splitTip : "Drag to resize.",
39589     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39590     useSplitTips : false,
39591
39592     applyConfig : function(config){
39593         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39594     },
39595     
39596     onRender : function(ctr,pos) {
39597         
39598         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39599         if(!this.config.split){
39600             return;
39601         }
39602         if(!this.split){
39603             
39604             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39605                             tag: "div",
39606                             id: this.el.id + "-split",
39607                             cls: "roo-layout-split roo-layout-split-"+this.position,
39608                             html: "&#160;"
39609             });
39610             /** The SplitBar for this region 
39611             * @type Roo.SplitBar */
39612             // does not exist yet...
39613             Roo.log([this.position, this.orientation]);
39614             
39615             this.split = new Roo.bootstrap.SplitBar({
39616                 dragElement : splitEl,
39617                 resizingElement: this.el,
39618                 orientation : this.orientation
39619             });
39620             
39621             this.split.on("moved", this.onSplitMove, this);
39622             this.split.useShim = this.config.useShim === true;
39623             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39624             if(this.useSplitTips){
39625                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39626             }
39627             //if(config.collapsible){
39628             //    this.split.el.on("dblclick", this.collapse,  this);
39629             //}
39630         }
39631         if(typeof this.config.minSize != "undefined"){
39632             this.split.minSize = this.config.minSize;
39633         }
39634         if(typeof this.config.maxSize != "undefined"){
39635             this.split.maxSize = this.config.maxSize;
39636         }
39637         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39638             this.hideSplitter();
39639         }
39640         
39641     },
39642
39643     getHMaxSize : function(){
39644          var cmax = this.config.maxSize || 10000;
39645          var center = this.mgr.getRegion("center");
39646          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39647     },
39648
39649     getVMaxSize : function(){
39650          var cmax = this.config.maxSize || 10000;
39651          var center = this.mgr.getRegion("center");
39652          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39653     },
39654
39655     onSplitMove : function(split, newSize){
39656         this.fireEvent("resized", this, newSize);
39657     },
39658     
39659     /** 
39660      * Returns the {@link Roo.SplitBar} for this region.
39661      * @return {Roo.SplitBar}
39662      */
39663     getSplitBar : function(){
39664         return this.split;
39665     },
39666     
39667     hide : function(){
39668         this.hideSplitter();
39669         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39670     },
39671
39672     hideSplitter : function(){
39673         if(this.split){
39674             this.split.el.setLocation(-2000,-2000);
39675             this.split.el.hide();
39676         }
39677     },
39678
39679     show : function(){
39680         if(this.split){
39681             this.split.el.show();
39682         }
39683         Roo.bootstrap.layout.Split.superclass.show.call(this);
39684     },
39685     
39686     beforeSlide: function(){
39687         if(Roo.isGecko){// firefox overflow auto bug workaround
39688             this.bodyEl.clip();
39689             if(this.tabs) {
39690                 this.tabs.bodyEl.clip();
39691             }
39692             if(this.activePanel){
39693                 this.activePanel.getEl().clip();
39694                 
39695                 if(this.activePanel.beforeSlide){
39696                     this.activePanel.beforeSlide();
39697                 }
39698             }
39699         }
39700     },
39701     
39702     afterSlide : function(){
39703         if(Roo.isGecko){// firefox overflow auto bug workaround
39704             this.bodyEl.unclip();
39705             if(this.tabs) {
39706                 this.tabs.bodyEl.unclip();
39707             }
39708             if(this.activePanel){
39709                 this.activePanel.getEl().unclip();
39710                 if(this.activePanel.afterSlide){
39711                     this.activePanel.afterSlide();
39712                 }
39713             }
39714         }
39715     },
39716
39717     initAutoHide : function(){
39718         if(this.autoHide !== false){
39719             if(!this.autoHideHd){
39720                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39721                 this.autoHideHd = {
39722                     "mouseout": function(e){
39723                         if(!e.within(this.el, true)){
39724                             st.delay(500);
39725                         }
39726                     },
39727                     "mouseover" : function(e){
39728                         st.cancel();
39729                     },
39730                     scope : this
39731                 };
39732             }
39733             this.el.on(this.autoHideHd);
39734         }
39735     },
39736
39737     clearAutoHide : function(){
39738         if(this.autoHide !== false){
39739             this.el.un("mouseout", this.autoHideHd.mouseout);
39740             this.el.un("mouseover", this.autoHideHd.mouseover);
39741         }
39742     },
39743
39744     clearMonitor : function(){
39745         Roo.get(document).un("click", this.slideInIf, this);
39746     },
39747
39748     // these names are backwards but not changed for compat
39749     slideOut : function(){
39750         if(this.isSlid || this.el.hasActiveFx()){
39751             return;
39752         }
39753         this.isSlid = true;
39754         if(this.collapseBtn){
39755             this.collapseBtn.hide();
39756         }
39757         this.closeBtnState = this.closeBtn.getStyle('display');
39758         this.closeBtn.hide();
39759         if(this.stickBtn){
39760             this.stickBtn.show();
39761         }
39762         this.el.show();
39763         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39764         this.beforeSlide();
39765         this.el.setStyle("z-index", 10001);
39766         this.el.slideIn(this.getSlideAnchor(), {
39767             callback: function(){
39768                 this.afterSlide();
39769                 this.initAutoHide();
39770                 Roo.get(document).on("click", this.slideInIf, this);
39771                 this.fireEvent("slideshow", this);
39772             },
39773             scope: this,
39774             block: true
39775         });
39776     },
39777
39778     afterSlideIn : function(){
39779         this.clearAutoHide();
39780         this.isSlid = false;
39781         this.clearMonitor();
39782         this.el.setStyle("z-index", "");
39783         if(this.collapseBtn){
39784             this.collapseBtn.show();
39785         }
39786         this.closeBtn.setStyle('display', this.closeBtnState);
39787         if(this.stickBtn){
39788             this.stickBtn.hide();
39789         }
39790         this.fireEvent("slidehide", this);
39791     },
39792
39793     slideIn : function(cb){
39794         if(!this.isSlid || this.el.hasActiveFx()){
39795             Roo.callback(cb);
39796             return;
39797         }
39798         this.isSlid = false;
39799         this.beforeSlide();
39800         this.el.slideOut(this.getSlideAnchor(), {
39801             callback: function(){
39802                 this.el.setLeftTop(-10000, -10000);
39803                 this.afterSlide();
39804                 this.afterSlideIn();
39805                 Roo.callback(cb);
39806             },
39807             scope: this,
39808             block: true
39809         });
39810     },
39811     
39812     slideInIf : function(e){
39813         if(!e.within(this.el)){
39814             this.slideIn();
39815         }
39816     },
39817
39818     animateCollapse : function(){
39819         this.beforeSlide();
39820         this.el.setStyle("z-index", 20000);
39821         var anchor = this.getSlideAnchor();
39822         this.el.slideOut(anchor, {
39823             callback : function(){
39824                 this.el.setStyle("z-index", "");
39825                 this.collapsedEl.slideIn(anchor, {duration:.3});
39826                 this.afterSlide();
39827                 this.el.setLocation(-10000,-10000);
39828                 this.el.hide();
39829                 this.fireEvent("collapsed", this);
39830             },
39831             scope: this,
39832             block: true
39833         });
39834     },
39835
39836     animateExpand : function(){
39837         this.beforeSlide();
39838         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39839         this.el.setStyle("z-index", 20000);
39840         this.collapsedEl.hide({
39841             duration:.1
39842         });
39843         this.el.slideIn(this.getSlideAnchor(), {
39844             callback : function(){
39845                 this.el.setStyle("z-index", "");
39846                 this.afterSlide();
39847                 if(this.split){
39848                     this.split.el.show();
39849                 }
39850                 this.fireEvent("invalidated", this);
39851                 this.fireEvent("expanded", this);
39852             },
39853             scope: this,
39854             block: true
39855         });
39856     },
39857
39858     anchors : {
39859         "west" : "left",
39860         "east" : "right",
39861         "north" : "top",
39862         "south" : "bottom"
39863     },
39864
39865     sanchors : {
39866         "west" : "l",
39867         "east" : "r",
39868         "north" : "t",
39869         "south" : "b"
39870     },
39871
39872     canchors : {
39873         "west" : "tl-tr",
39874         "east" : "tr-tl",
39875         "north" : "tl-bl",
39876         "south" : "bl-tl"
39877     },
39878
39879     getAnchor : function(){
39880         return this.anchors[this.position];
39881     },
39882
39883     getCollapseAnchor : function(){
39884         return this.canchors[this.position];
39885     },
39886
39887     getSlideAnchor : function(){
39888         return this.sanchors[this.position];
39889     },
39890
39891     getAlignAdj : function(){
39892         var cm = this.cmargins;
39893         switch(this.position){
39894             case "west":
39895                 return [0, 0];
39896             break;
39897             case "east":
39898                 return [0, 0];
39899             break;
39900             case "north":
39901                 return [0, 0];
39902             break;
39903             case "south":
39904                 return [0, 0];
39905             break;
39906         }
39907     },
39908
39909     getExpandAdj : function(){
39910         var c = this.collapsedEl, cm = this.cmargins;
39911         switch(this.position){
39912             case "west":
39913                 return [-(cm.right+c.getWidth()+cm.left), 0];
39914             break;
39915             case "east":
39916                 return [cm.right+c.getWidth()+cm.left, 0];
39917             break;
39918             case "north":
39919                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39920             break;
39921             case "south":
39922                 return [0, cm.top+cm.bottom+c.getHeight()];
39923             break;
39924         }
39925     }
39926 });/*
39927  * Based on:
39928  * Ext JS Library 1.1.1
39929  * Copyright(c) 2006-2007, Ext JS, LLC.
39930  *
39931  * Originally Released Under LGPL - original licence link has changed is not relivant.
39932  *
39933  * Fork - LGPL
39934  * <script type="text/javascript">
39935  */
39936 /*
39937  * These classes are private internal classes
39938  */
39939 Roo.bootstrap.layout.Center = function(config){
39940     config.region = "center";
39941     Roo.bootstrap.layout.Region.call(this, config);
39942     this.visible = true;
39943     this.minWidth = config.minWidth || 20;
39944     this.minHeight = config.minHeight || 20;
39945 };
39946
39947 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39948     hide : function(){
39949         // center panel can't be hidden
39950     },
39951     
39952     show : function(){
39953         // center panel can't be hidden
39954     },
39955     
39956     getMinWidth: function(){
39957         return this.minWidth;
39958     },
39959     
39960     getMinHeight: function(){
39961         return this.minHeight;
39962     }
39963 });
39964
39965
39966
39967
39968  
39969
39970
39971
39972
39973
39974
39975 Roo.bootstrap.layout.North = function(config)
39976 {
39977     config.region = 'north';
39978     config.cursor = 'n-resize';
39979     
39980     Roo.bootstrap.layout.Split.call(this, config);
39981     
39982     
39983     if(this.split){
39984         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39985         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39986         this.split.el.addClass("roo-layout-split-v");
39987     }
39988     //var size = config.initialSize || config.height;
39989     //if(this.el && typeof size != "undefined"){
39990     //    this.el.setHeight(size);
39991     //}
39992 };
39993 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39994 {
39995     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39996      
39997      
39998     onRender : function(ctr, pos)
39999     {
40000         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40001         var size = this.config.initialSize || this.config.height;
40002         if(this.el && typeof size != "undefined"){
40003             this.el.setHeight(size);
40004         }
40005     
40006     },
40007     
40008     getBox : function(){
40009         if(this.collapsed){
40010             return this.collapsedEl.getBox();
40011         }
40012         var box = this.el.getBox();
40013         if(this.split){
40014             box.height += this.split.el.getHeight();
40015         }
40016         return box;
40017     },
40018     
40019     updateBox : function(box){
40020         if(this.split && !this.collapsed){
40021             box.height -= this.split.el.getHeight();
40022             this.split.el.setLeft(box.x);
40023             this.split.el.setTop(box.y+box.height);
40024             this.split.el.setWidth(box.width);
40025         }
40026         if(this.collapsed){
40027             this.updateBody(box.width, null);
40028         }
40029         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40030     }
40031 });
40032
40033
40034
40035
40036
40037 Roo.bootstrap.layout.South = function(config){
40038     config.region = 'south';
40039     config.cursor = 's-resize';
40040     Roo.bootstrap.layout.Split.call(this, config);
40041     if(this.split){
40042         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40043         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40044         this.split.el.addClass("roo-layout-split-v");
40045     }
40046     
40047 };
40048
40049 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40050     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40051     
40052     onRender : function(ctr, pos)
40053     {
40054         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40055         var size = this.config.initialSize || this.config.height;
40056         if(this.el && typeof size != "undefined"){
40057             this.el.setHeight(size);
40058         }
40059     
40060     },
40061     
40062     getBox : function(){
40063         if(this.collapsed){
40064             return this.collapsedEl.getBox();
40065         }
40066         var box = this.el.getBox();
40067         if(this.split){
40068             var sh = this.split.el.getHeight();
40069             box.height += sh;
40070             box.y -= sh;
40071         }
40072         return box;
40073     },
40074     
40075     updateBox : function(box){
40076         if(this.split && !this.collapsed){
40077             var sh = this.split.el.getHeight();
40078             box.height -= sh;
40079             box.y += sh;
40080             this.split.el.setLeft(box.x);
40081             this.split.el.setTop(box.y-sh);
40082             this.split.el.setWidth(box.width);
40083         }
40084         if(this.collapsed){
40085             this.updateBody(box.width, null);
40086         }
40087         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40088     }
40089 });
40090
40091 Roo.bootstrap.layout.East = function(config){
40092     config.region = "east";
40093     config.cursor = "e-resize";
40094     Roo.bootstrap.layout.Split.call(this, config);
40095     if(this.split){
40096         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40097         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40098         this.split.el.addClass("roo-layout-split-h");
40099     }
40100     
40101 };
40102 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40103     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40104     
40105     onRender : function(ctr, pos)
40106     {
40107         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40108         var size = this.config.initialSize || this.config.width;
40109         if(this.el && typeof size != "undefined"){
40110             this.el.setWidth(size);
40111         }
40112     
40113     },
40114     
40115     getBox : function(){
40116         if(this.collapsed){
40117             return this.collapsedEl.getBox();
40118         }
40119         var box = this.el.getBox();
40120         if(this.split){
40121             var sw = this.split.el.getWidth();
40122             box.width += sw;
40123             box.x -= sw;
40124         }
40125         return box;
40126     },
40127
40128     updateBox : function(box){
40129         if(this.split && !this.collapsed){
40130             var sw = this.split.el.getWidth();
40131             box.width -= sw;
40132             this.split.el.setLeft(box.x);
40133             this.split.el.setTop(box.y);
40134             this.split.el.setHeight(box.height);
40135             box.x += sw;
40136         }
40137         if(this.collapsed){
40138             this.updateBody(null, box.height);
40139         }
40140         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40141     }
40142 });
40143
40144 Roo.bootstrap.layout.West = function(config){
40145     config.region = "west";
40146     config.cursor = "w-resize";
40147     
40148     Roo.bootstrap.layout.Split.call(this, config);
40149     if(this.split){
40150         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40151         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40152         this.split.el.addClass("roo-layout-split-h");
40153     }
40154     
40155 };
40156 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40157     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40158     
40159     onRender: function(ctr, pos)
40160     {
40161         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40162         var size = this.config.initialSize || this.config.width;
40163         if(typeof size != "undefined"){
40164             this.el.setWidth(size);
40165         }
40166     },
40167     
40168     getBox : function(){
40169         if(this.collapsed){
40170             return this.collapsedEl.getBox();
40171         }
40172         var box = this.el.getBox();
40173         if (box.width == 0) {
40174             box.width = this.config.width; // kludge?
40175         }
40176         if(this.split){
40177             box.width += this.split.el.getWidth();
40178         }
40179         return box;
40180     },
40181     
40182     updateBox : function(box){
40183         if(this.split && !this.collapsed){
40184             var sw = this.split.el.getWidth();
40185             box.width -= sw;
40186             this.split.el.setLeft(box.x+box.width);
40187             this.split.el.setTop(box.y);
40188             this.split.el.setHeight(box.height);
40189         }
40190         if(this.collapsed){
40191             this.updateBody(null, box.height);
40192         }
40193         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40194     }
40195 });/*
40196  * Based on:
40197  * Ext JS Library 1.1.1
40198  * Copyright(c) 2006-2007, Ext JS, LLC.
40199  *
40200  * Originally Released Under LGPL - original licence link has changed is not relivant.
40201  *
40202  * Fork - LGPL
40203  * <script type="text/javascript">
40204  */
40205 /**
40206  * @class Roo.bootstrap.paenl.Content
40207  * @extends Roo.util.Observable
40208  * @builder-top
40209  * @children Roo.bootstrap.Component
40210  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40211  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40212  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40213  * @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
40214  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40215  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40216  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40217  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40218  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40219  * @cfg {String} title          The title for this panel
40220  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40221  * @cfg {String} url            Calls {@link #setUrl} with this value
40222  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40223  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40224  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40225  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40226  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40227  * @cfg {Boolean} badges render the badges
40228  * @cfg {String} cls  extra classes to use  
40229  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40230  
40231  * @constructor
40232  * Create a new ContentPanel.
40233  * @param {String/Object} config A string to set only the title or a config object
40234  
40235  */
40236 Roo.bootstrap.panel.Content = function( config){
40237     
40238     this.tpl = config.tpl || false;
40239     
40240     var el = config.el;
40241     var content = config.content;
40242
40243     if(config.autoCreate){ // xtype is available if this is called from factory
40244         el = Roo.id();
40245     }
40246     this.el = Roo.get(el);
40247     if(!this.el && config && config.autoCreate){
40248         if(typeof config.autoCreate == "object"){
40249             if(!config.autoCreate.id){
40250                 config.autoCreate.id = config.id||el;
40251             }
40252             this.el = Roo.DomHelper.append(document.body,
40253                         config.autoCreate, true);
40254         }else{
40255             var elcfg =  {
40256                 tag: "div",
40257                 cls: (config.cls || '') +
40258                     (config.background ? ' bg-' + config.background : '') +
40259                     " roo-layout-inactive-content",
40260                 id: config.id||el
40261             };
40262             if (config.iframe) {
40263                 elcfg.cn = [
40264                     {
40265                         tag : 'iframe',
40266                         style : 'border: 0px',
40267                         src : 'about:blank'
40268                     }
40269                 ];
40270             }
40271               
40272             if (config.html) {
40273                 elcfg.html = config.html;
40274                 
40275             }
40276                         
40277             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40278             if (config.iframe) {
40279                 this.iframeEl = this.el.select('iframe',true).first();
40280             }
40281             
40282         }
40283     } 
40284     this.closable = false;
40285     this.loaded = false;
40286     this.active = false;
40287    
40288       
40289     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40290         
40291         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40292         
40293         this.wrapEl = this.el; //this.el.wrap();
40294         var ti = [];
40295         if (config.toolbar.items) {
40296             ti = config.toolbar.items ;
40297             delete config.toolbar.items ;
40298         }
40299         
40300         var nitems = [];
40301         this.toolbar.render(this.wrapEl, 'before');
40302         for(var i =0;i < ti.length;i++) {
40303           //  Roo.log(['add child', items[i]]);
40304             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40305         }
40306         this.toolbar.items = nitems;
40307         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40308         delete config.toolbar;
40309         
40310     }
40311     /*
40312     // xtype created footer. - not sure if will work as we normally have to render first..
40313     if (this.footer && !this.footer.el && this.footer.xtype) {
40314         if (!this.wrapEl) {
40315             this.wrapEl = this.el.wrap();
40316         }
40317     
40318         this.footer.container = this.wrapEl.createChild();
40319          
40320         this.footer = Roo.factory(this.footer, Roo);
40321         
40322     }
40323     */
40324     
40325      if(typeof config == "string"){
40326         this.title = config;
40327     }else{
40328         Roo.apply(this, config);
40329     }
40330     
40331     if(this.resizeEl){
40332         this.resizeEl = Roo.get(this.resizeEl, true);
40333     }else{
40334         this.resizeEl = this.el;
40335     }
40336     // handle view.xtype
40337     
40338  
40339     
40340     
40341     this.addEvents({
40342         /**
40343          * @event activate
40344          * Fires when this panel is activated. 
40345          * @param {Roo.ContentPanel} this
40346          */
40347         "activate" : true,
40348         /**
40349          * @event deactivate
40350          * Fires when this panel is activated. 
40351          * @param {Roo.ContentPanel} this
40352          */
40353         "deactivate" : true,
40354
40355         /**
40356          * @event resize
40357          * Fires when this panel is resized if fitToFrame is true.
40358          * @param {Roo.ContentPanel} this
40359          * @param {Number} width The width after any component adjustments
40360          * @param {Number} height The height after any component adjustments
40361          */
40362         "resize" : true,
40363         
40364          /**
40365          * @event render
40366          * Fires when this tab is created
40367          * @param {Roo.ContentPanel} this
40368          */
40369         "render" : true,
40370         
40371           /**
40372          * @event scroll
40373          * Fires when this content is scrolled
40374          * @param {Roo.ContentPanel} this
40375          * @param {Event} scrollEvent
40376          */
40377         "scroll" : true
40378         
40379         
40380         
40381     });
40382     
40383
40384     
40385     
40386     if(this.autoScroll && !this.iframe){
40387         this.resizeEl.setStyle("overflow", "auto");
40388         this.resizeEl.on('scroll', this.onScroll, this);
40389     } else {
40390         // fix randome scrolling
40391         //this.el.on('scroll', function() {
40392         //    Roo.log('fix random scolling');
40393         //    this.scrollTo('top',0); 
40394         //});
40395     }
40396     content = content || this.content;
40397     if(content){
40398         this.setContent(content);
40399     }
40400     if(config && config.url){
40401         this.setUrl(this.url, this.params, this.loadOnce);
40402     }
40403     
40404     
40405     
40406     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40407     
40408     if (this.view && typeof(this.view.xtype) != 'undefined') {
40409         this.view.el = this.el.appendChild(document.createElement("div"));
40410         this.view = Roo.factory(this.view); 
40411         this.view.render  &&  this.view.render(false, '');  
40412     }
40413     
40414     
40415     this.fireEvent('render', this);
40416 };
40417
40418 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40419     
40420     cls : '',
40421     background : '',
40422     
40423     tabTip : '',
40424     
40425     iframe : false,
40426     iframeEl : false,
40427     
40428     /* Resize Element - use this to work out scroll etc. */
40429     resizeEl : false,
40430     
40431     setRegion : function(region){
40432         this.region = region;
40433         this.setActiveClass(region && !this.background);
40434     },
40435     
40436     
40437     setActiveClass: function(state)
40438     {
40439         if(state){
40440            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40441            this.el.setStyle('position','relative');
40442         }else{
40443            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40444            this.el.setStyle('position', 'absolute');
40445         } 
40446     },
40447     
40448     /**
40449      * Returns the toolbar for this Panel if one was configured. 
40450      * @return {Roo.Toolbar} 
40451      */
40452     getToolbar : function(){
40453         return this.toolbar;
40454     },
40455     
40456     setActiveState : function(active)
40457     {
40458         this.active = active;
40459         this.setActiveClass(active);
40460         if(!active){
40461             if(this.fireEvent("deactivate", this) === false){
40462                 return false;
40463             }
40464             return true;
40465         }
40466         this.fireEvent("activate", this);
40467         return true;
40468     },
40469     /**
40470      * Updates this panel's element (not for iframe)
40471      * @param {String} content The new content
40472      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40473     */
40474     setContent : function(content, loadScripts){
40475         if (this.iframe) {
40476             return;
40477         }
40478         
40479         this.el.update(content, loadScripts);
40480     },
40481
40482     ignoreResize : function(w, h){
40483         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40484             return true;
40485         }else{
40486             this.lastSize = {width: w, height: h};
40487             return false;
40488         }
40489     },
40490     /**
40491      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40492      * @return {Roo.UpdateManager} The UpdateManager
40493      */
40494     getUpdateManager : function(){
40495         if (this.iframe) {
40496             return false;
40497         }
40498         return this.el.getUpdateManager();
40499     },
40500      /**
40501      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40502      * Does not work with IFRAME contents
40503      * @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:
40504 <pre><code>
40505 panel.load({
40506     url: "your-url.php",
40507     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40508     callback: yourFunction,
40509     scope: yourObject, //(optional scope)
40510     discardUrl: false,
40511     nocache: false,
40512     text: "Loading...",
40513     timeout: 30,
40514     scripts: false
40515 });
40516 </code></pre>
40517      
40518      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40519      * 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.
40520      * @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}
40521      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40522      * @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.
40523      * @return {Roo.ContentPanel} this
40524      */
40525     load : function(){
40526         
40527         if (this.iframe) {
40528             return this;
40529         }
40530         
40531         var um = this.el.getUpdateManager();
40532         um.update.apply(um, arguments);
40533         return this;
40534     },
40535
40536
40537     /**
40538      * 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.
40539      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40540      * @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)
40541      * @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)
40542      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40543      */
40544     setUrl : function(url, params, loadOnce){
40545         if (this.iframe) {
40546             this.iframeEl.dom.src = url;
40547             return false;
40548         }
40549         
40550         if(this.refreshDelegate){
40551             this.removeListener("activate", this.refreshDelegate);
40552         }
40553         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40554         this.on("activate", this.refreshDelegate);
40555         return this.el.getUpdateManager();
40556     },
40557     
40558     _handleRefresh : function(url, params, loadOnce){
40559         if(!loadOnce || !this.loaded){
40560             var updater = this.el.getUpdateManager();
40561             updater.update(url, params, this._setLoaded.createDelegate(this));
40562         }
40563     },
40564     
40565     _setLoaded : function(){
40566         this.loaded = true;
40567     }, 
40568     
40569     /**
40570      * Returns this panel's id
40571      * @return {String} 
40572      */
40573     getId : function(){
40574         return this.el.id;
40575     },
40576     
40577     /** 
40578      * Returns this panel's element - used by regiosn to add.
40579      * @return {Roo.Element} 
40580      */
40581     getEl : function(){
40582         return this.wrapEl || this.el;
40583     },
40584     
40585    
40586     
40587     adjustForComponents : function(width, height)
40588     {
40589         //Roo.log('adjustForComponents ');
40590         if(this.resizeEl != this.el){
40591             width -= this.el.getFrameWidth('lr');
40592             height -= this.el.getFrameWidth('tb');
40593         }
40594         if(this.toolbar){
40595             var te = this.toolbar.getEl();
40596             te.setWidth(width);
40597             height -= te.getHeight();
40598         }
40599         if(this.footer){
40600             var te = this.footer.getEl();
40601             te.setWidth(width);
40602             height -= te.getHeight();
40603         }
40604         
40605         
40606         if(this.adjustments){
40607             width += this.adjustments[0];
40608             height += this.adjustments[1];
40609         }
40610         return {"width": width, "height": height};
40611     },
40612     
40613     setSize : function(width, height){
40614         if(this.fitToFrame && !this.ignoreResize(width, height)){
40615             if(this.fitContainer && this.resizeEl != this.el){
40616                 this.el.setSize(width, height);
40617             }
40618             var size = this.adjustForComponents(width, height);
40619             if (this.iframe) {
40620                 this.iframeEl.setSize(width,height);
40621             }
40622             
40623             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40624             this.fireEvent('resize', this, size.width, size.height);
40625             
40626             
40627         }
40628     },
40629     
40630     /**
40631      * Returns this panel's title
40632      * @return {String} 
40633      */
40634     getTitle : function(){
40635         
40636         if (typeof(this.title) != 'object') {
40637             return this.title;
40638         }
40639         
40640         var t = '';
40641         for (var k in this.title) {
40642             if (!this.title.hasOwnProperty(k)) {
40643                 continue;
40644             }
40645             
40646             if (k.indexOf('-') >= 0) {
40647                 var s = k.split('-');
40648                 for (var i = 0; i<s.length; i++) {
40649                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40650                 }
40651             } else {
40652                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40653             }
40654         }
40655         return t;
40656     },
40657     
40658     /**
40659      * Set this panel's title
40660      * @param {String} title
40661      */
40662     setTitle : function(title){
40663         this.title = title;
40664         if(this.region){
40665             this.region.updatePanelTitle(this, title);
40666         }
40667     },
40668     
40669     /**
40670      * Returns true is this panel was configured to be closable
40671      * @return {Boolean} 
40672      */
40673     isClosable : function(){
40674         return this.closable;
40675     },
40676     
40677     beforeSlide : function(){
40678         this.el.clip();
40679         this.resizeEl.clip();
40680     },
40681     
40682     afterSlide : function(){
40683         this.el.unclip();
40684         this.resizeEl.unclip();
40685     },
40686     
40687     /**
40688      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40689      *   Will fail silently if the {@link #setUrl} method has not been called.
40690      *   This does not activate the panel, just updates its content.
40691      */
40692     refresh : function(){
40693         if(this.refreshDelegate){
40694            this.loaded = false;
40695            this.refreshDelegate();
40696         }
40697     },
40698     
40699     /**
40700      * Destroys this panel
40701      */
40702     destroy : function(){
40703         this.el.removeAllListeners();
40704         var tempEl = document.createElement("span");
40705         tempEl.appendChild(this.el.dom);
40706         tempEl.innerHTML = "";
40707         this.el.remove();
40708         this.el = null;
40709     },
40710     
40711     /**
40712      * form - if the content panel contains a form - this is a reference to it.
40713      * @type {Roo.form.Form}
40714      */
40715     form : false,
40716     /**
40717      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40718      *    This contains a reference to it.
40719      * @type {Roo.View}
40720      */
40721     view : false,
40722     
40723       /**
40724      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40725      * <pre><code>
40726
40727 layout.addxtype({
40728        xtype : 'Form',
40729        items: [ .... ]
40730    }
40731 );
40732
40733 </code></pre>
40734      * @param {Object} cfg Xtype definition of item to add.
40735      */
40736     
40737     
40738     getChildContainer: function () {
40739         return this.getEl();
40740     },
40741     
40742     
40743     onScroll : function(e)
40744     {
40745         this.fireEvent('scroll', this, e);
40746     }
40747     
40748     
40749     /*
40750         var  ret = new Roo.factory(cfg);
40751         return ret;
40752         
40753         
40754         // add form..
40755         if (cfg.xtype.match(/^Form$/)) {
40756             
40757             var el;
40758             //if (this.footer) {
40759             //    el = this.footer.container.insertSibling(false, 'before');
40760             //} else {
40761                 el = this.el.createChild();
40762             //}
40763
40764             this.form = new  Roo.form.Form(cfg);
40765             
40766             
40767             if ( this.form.allItems.length) {
40768                 this.form.render(el.dom);
40769             }
40770             return this.form;
40771         }
40772         // should only have one of theses..
40773         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40774             // views.. should not be just added - used named prop 'view''
40775             
40776             cfg.el = this.el.appendChild(document.createElement("div"));
40777             // factory?
40778             
40779             var ret = new Roo.factory(cfg);
40780              
40781              ret.render && ret.render(false, ''); // render blank..
40782             this.view = ret;
40783             return ret;
40784         }
40785         return false;
40786     }
40787     \*/
40788 });
40789  
40790 /**
40791  * @class Roo.bootstrap.panel.Grid
40792  * @extends Roo.bootstrap.panel.Content
40793  * @constructor
40794  * Create a new GridPanel.
40795  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40796  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40797  * @param {Object} config A the config object
40798   
40799  */
40800
40801
40802
40803 Roo.bootstrap.panel.Grid = function(config)
40804 {
40805     
40806       
40807     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40808         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40809
40810     config.el = this.wrapper;
40811     //this.el = this.wrapper;
40812     
40813       if (config.container) {
40814         // ctor'ed from a Border/panel.grid
40815         
40816         
40817         this.wrapper.setStyle("overflow", "hidden");
40818         this.wrapper.addClass('roo-grid-container');
40819
40820     }
40821     
40822     
40823     if(config.toolbar){
40824         var tool_el = this.wrapper.createChild();    
40825         this.toolbar = Roo.factory(config.toolbar);
40826         var ti = [];
40827         if (config.toolbar.items) {
40828             ti = config.toolbar.items ;
40829             delete config.toolbar.items ;
40830         }
40831         
40832         var nitems = [];
40833         this.toolbar.render(tool_el);
40834         for(var i =0;i < ti.length;i++) {
40835           //  Roo.log(['add child', items[i]]);
40836             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40837         }
40838         this.toolbar.items = nitems;
40839         
40840         delete config.toolbar;
40841     }
40842     
40843     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40844     config.grid.scrollBody = true;;
40845     config.grid.monitorWindowResize = false; // turn off autosizing
40846     config.grid.autoHeight = false;
40847     config.grid.autoWidth = false;
40848     
40849     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40850     
40851     if (config.background) {
40852         // render grid on panel activation (if panel background)
40853         this.on('activate', function(gp) {
40854             if (!gp.grid.rendered) {
40855                 gp.grid.render(this.wrapper);
40856                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40857             }
40858         });
40859             
40860     } else {
40861         this.grid.render(this.wrapper);
40862         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40863
40864     }
40865     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40866     // ??? needed ??? config.el = this.wrapper;
40867     
40868     
40869     
40870   
40871     // xtype created footer. - not sure if will work as we normally have to render first..
40872     if (this.footer && !this.footer.el && this.footer.xtype) {
40873         
40874         var ctr = this.grid.getView().getFooterPanel(true);
40875         this.footer.dataSource = this.grid.dataSource;
40876         this.footer = Roo.factory(this.footer, Roo);
40877         this.footer.render(ctr);
40878         
40879     }
40880     
40881     
40882     
40883     
40884      
40885 };
40886
40887 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40888     getId : function(){
40889         return this.grid.id;
40890     },
40891     
40892     /**
40893      * Returns the grid for this panel
40894      * @return {Roo.bootstrap.Table} 
40895      */
40896     getGrid : function(){
40897         return this.grid;    
40898     },
40899     
40900     setSize : function(width, height){
40901         if(!this.ignoreResize(width, height)){
40902             var grid = this.grid;
40903             var size = this.adjustForComponents(width, height);
40904             // tfoot is not a footer?
40905           
40906             
40907             var gridel = grid.getGridEl();
40908             gridel.setSize(size.width, size.height);
40909             
40910             var tbd = grid.getGridEl().select('tbody', true).first();
40911             var thd = grid.getGridEl().select('thead',true).first();
40912             var tbf= grid.getGridEl().select('tfoot', true).first();
40913
40914             if (tbf) {
40915                 size.height -= tbf.getHeight();
40916             }
40917             if (thd) {
40918                 size.height -= thd.getHeight();
40919             }
40920             
40921             tbd.setSize(size.width, size.height );
40922             // this is for the account management tab -seems to work there.
40923             var thd = grid.getGridEl().select('thead',true).first();
40924             //if (tbd) {
40925             //    tbd.setSize(size.width, size.height - thd.getHeight());
40926             //}
40927              
40928             grid.autoSize();
40929         }
40930     },
40931      
40932     
40933     
40934     beforeSlide : function(){
40935         this.grid.getView().scroller.clip();
40936     },
40937     
40938     afterSlide : function(){
40939         this.grid.getView().scroller.unclip();
40940     },
40941     
40942     destroy : function(){
40943         this.grid.destroy();
40944         delete this.grid;
40945         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40946     }
40947 });
40948
40949 /**
40950  * @class Roo.bootstrap.panel.Nest
40951  * @extends Roo.bootstrap.panel.Content
40952  * @constructor
40953  * Create a new Panel, that can contain a layout.Border.
40954  * 
40955  * 
40956  * @param {String/Object} config A string to set only the title or a config object
40957  */
40958 Roo.bootstrap.panel.Nest = function(config)
40959 {
40960     // construct with only one argument..
40961     /* FIXME - implement nicer consturctors
40962     if (layout.layout) {
40963         config = layout;
40964         layout = config.layout;
40965         delete config.layout;
40966     }
40967     if (layout.xtype && !layout.getEl) {
40968         // then layout needs constructing..
40969         layout = Roo.factory(layout, Roo);
40970     }
40971     */
40972     
40973     config.el =  config.layout.getEl();
40974     
40975     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40976     
40977     config.layout.monitorWindowResize = false; // turn off autosizing
40978     this.layout = config.layout;
40979     this.layout.getEl().addClass("roo-layout-nested-layout");
40980     this.layout.parent = this;
40981     
40982     
40983     
40984     
40985 };
40986
40987 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40988     /**
40989     * @cfg {Roo.BorderLayout} layout The layout for this panel
40990     */
40991     layout : false,
40992
40993     setSize : function(width, height){
40994         if(!this.ignoreResize(width, height)){
40995             var size = this.adjustForComponents(width, height);
40996             var el = this.layout.getEl();
40997             if (size.height < 1) {
40998                 el.setWidth(size.width);   
40999             } else {
41000                 el.setSize(size.width, size.height);
41001             }
41002             var touch = el.dom.offsetWidth;
41003             this.layout.layout();
41004             // ie requires a double layout on the first pass
41005             if(Roo.isIE && !this.initialized){
41006                 this.initialized = true;
41007                 this.layout.layout();
41008             }
41009         }
41010     },
41011     
41012     // activate all subpanels if not currently active..
41013     
41014     setActiveState : function(active){
41015         this.active = active;
41016         this.setActiveClass(active);
41017         
41018         if(!active){
41019             this.fireEvent("deactivate", this);
41020             return;
41021         }
41022         
41023         this.fireEvent("activate", this);
41024         // not sure if this should happen before or after..
41025         if (!this.layout) {
41026             return; // should not happen..
41027         }
41028         var reg = false;
41029         for (var r in this.layout.regions) {
41030             reg = this.layout.getRegion(r);
41031             if (reg.getActivePanel()) {
41032                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41033                 reg.setActivePanel(reg.getActivePanel());
41034                 continue;
41035             }
41036             if (!reg.panels.length) {
41037                 continue;
41038             }
41039             reg.showPanel(reg.getPanel(0));
41040         }
41041         
41042         
41043         
41044         
41045     },
41046     
41047     /**
41048      * Returns the nested BorderLayout for this panel
41049      * @return {Roo.BorderLayout} 
41050      */
41051     getLayout : function(){
41052         return this.layout;
41053     },
41054     
41055      /**
41056      * Adds a xtype elements to the layout of the nested panel
41057      * <pre><code>
41058
41059 panel.addxtype({
41060        xtype : 'ContentPanel',
41061        region: 'west',
41062        items: [ .... ]
41063    }
41064 );
41065
41066 panel.addxtype({
41067         xtype : 'NestedLayoutPanel',
41068         region: 'west',
41069         layout: {
41070            center: { },
41071            west: { }   
41072         },
41073         items : [ ... list of content panels or nested layout panels.. ]
41074    }
41075 );
41076 </code></pre>
41077      * @param {Object} cfg Xtype definition of item to add.
41078      */
41079     addxtype : function(cfg) {
41080         return this.layout.addxtype(cfg);
41081     
41082     }
41083 });/*
41084  * Based on:
41085  * Ext JS Library 1.1.1
41086  * Copyright(c) 2006-2007, Ext JS, LLC.
41087  *
41088  * Originally Released Under LGPL - original licence link has changed is not relivant.
41089  *
41090  * Fork - LGPL
41091  * <script type="text/javascript">
41092  */
41093 /**
41094  * @class Roo.TabPanel
41095  * @extends Roo.util.Observable
41096  * A lightweight tab container.
41097  * <br><br>
41098  * Usage:
41099  * <pre><code>
41100 // basic tabs 1, built from existing content
41101 var tabs = new Roo.TabPanel("tabs1");
41102 tabs.addTab("script", "View Script");
41103 tabs.addTab("markup", "View Markup");
41104 tabs.activate("script");
41105
41106 // more advanced tabs, built from javascript
41107 var jtabs = new Roo.TabPanel("jtabs");
41108 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41109
41110 // set up the UpdateManager
41111 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41112 var updater = tab2.getUpdateManager();
41113 updater.setDefaultUrl("ajax1.htm");
41114 tab2.on('activate', updater.refresh, updater, true);
41115
41116 // Use setUrl for Ajax loading
41117 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41118 tab3.setUrl("ajax2.htm", null, true);
41119
41120 // Disabled tab
41121 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41122 tab4.disable();
41123
41124 jtabs.activate("jtabs-1");
41125  * </code></pre>
41126  * @constructor
41127  * Create a new TabPanel.
41128  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41129  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41130  */
41131 Roo.bootstrap.panel.Tabs = function(config){
41132     /**
41133     * The container element for this TabPanel.
41134     * @type Roo.Element
41135     */
41136     this.el = Roo.get(config.el);
41137     delete config.el;
41138     if(config){
41139         if(typeof config == "boolean"){
41140             this.tabPosition = config ? "bottom" : "top";
41141         }else{
41142             Roo.apply(this, config);
41143         }
41144     }
41145     
41146     if(this.tabPosition == "bottom"){
41147         // if tabs are at the bottom = create the body first.
41148         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41149         this.el.addClass("roo-tabs-bottom");
41150     }
41151     // next create the tabs holders
41152     
41153     if (this.tabPosition == "west"){
41154         
41155         var reg = this.region; // fake it..
41156         while (reg) {
41157             if (!reg.mgr.parent) {
41158                 break;
41159             }
41160             reg = reg.mgr.parent.region;
41161         }
41162         Roo.log("got nest?");
41163         Roo.log(reg);
41164         if (reg.mgr.getRegion('west')) {
41165             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41166             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41167             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41168             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41169             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41170         
41171             
41172         }
41173         
41174         
41175     } else {
41176      
41177         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41178         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41179         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41180         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41181     }
41182     
41183     
41184     if(Roo.isIE){
41185         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41186     }
41187     
41188     // finally - if tabs are at the top, then create the body last..
41189     if(this.tabPosition != "bottom"){
41190         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41191          * @type Roo.Element
41192          */
41193         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41194         this.el.addClass("roo-tabs-top");
41195     }
41196     this.items = [];
41197
41198     this.bodyEl.setStyle("position", "relative");
41199
41200     this.active = null;
41201     this.activateDelegate = this.activate.createDelegate(this);
41202
41203     this.addEvents({
41204         /**
41205          * @event tabchange
41206          * Fires when the active tab changes
41207          * @param {Roo.TabPanel} this
41208          * @param {Roo.TabPanelItem} activePanel The new active tab
41209          */
41210         "tabchange": true,
41211         /**
41212          * @event beforetabchange
41213          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41214          * @param {Roo.TabPanel} this
41215          * @param {Object} e Set cancel to true on this object to cancel the tab change
41216          * @param {Roo.TabPanelItem} tab The tab being changed to
41217          */
41218         "beforetabchange" : true
41219     });
41220
41221     Roo.EventManager.onWindowResize(this.onResize, this);
41222     this.cpad = this.el.getPadding("lr");
41223     this.hiddenCount = 0;
41224
41225
41226     // toolbar on the tabbar support...
41227     if (this.toolbar) {
41228         alert("no toolbar support yet");
41229         this.toolbar  = false;
41230         /*
41231         var tcfg = this.toolbar;
41232         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41233         this.toolbar = new Roo.Toolbar(tcfg);
41234         if (Roo.isSafari) {
41235             var tbl = tcfg.container.child('table', true);
41236             tbl.setAttribute('width', '100%');
41237         }
41238         */
41239         
41240     }
41241    
41242
41243
41244     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41245 };
41246
41247 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41248     /*
41249      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41250      */
41251     tabPosition : "top",
41252     /*
41253      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41254      */
41255     currentTabWidth : 0,
41256     /*
41257      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41258      */
41259     minTabWidth : 40,
41260     /*
41261      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41262      */
41263     maxTabWidth : 250,
41264     /*
41265      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41266      */
41267     preferredTabWidth : 175,
41268     /*
41269      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41270      */
41271     resizeTabs : false,
41272     /*
41273      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41274      */
41275     monitorResize : true,
41276     /*
41277      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41278      */
41279     toolbar : false,  // set by caller..
41280     
41281     region : false, /// set by caller
41282     
41283     disableTooltips : true, // not used yet...
41284
41285     /**
41286      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41287      * @param {String} id The id of the div to use <b>or create</b>
41288      * @param {String} text The text for the tab
41289      * @param {String} content (optional) Content to put in the TabPanelItem body
41290      * @param {Boolean} closable (optional) True to create a close icon on the tab
41291      * @return {Roo.TabPanelItem} The created TabPanelItem
41292      */
41293     addTab : function(id, text, content, closable, tpl)
41294     {
41295         var item = new Roo.bootstrap.panel.TabItem({
41296             panel: this,
41297             id : id,
41298             text : text,
41299             closable : closable,
41300             tpl : tpl
41301         });
41302         this.addTabItem(item);
41303         if(content){
41304             item.setContent(content);
41305         }
41306         return item;
41307     },
41308
41309     /**
41310      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41311      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41312      * @return {Roo.TabPanelItem}
41313      */
41314     getTab : function(id){
41315         return this.items[id];
41316     },
41317
41318     /**
41319      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41320      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41321      */
41322     hideTab : function(id){
41323         var t = this.items[id];
41324         if(!t.isHidden()){
41325            t.setHidden(true);
41326            this.hiddenCount++;
41327            this.autoSizeTabs();
41328         }
41329     },
41330
41331     /**
41332      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41333      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41334      */
41335     unhideTab : function(id){
41336         var t = this.items[id];
41337         if(t.isHidden()){
41338            t.setHidden(false);
41339            this.hiddenCount--;
41340            this.autoSizeTabs();
41341         }
41342     },
41343
41344     /**
41345      * Adds an existing {@link Roo.TabPanelItem}.
41346      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41347      */
41348     addTabItem : function(item)
41349     {
41350         this.items[item.id] = item;
41351         this.items.push(item);
41352         this.autoSizeTabs();
41353       //  if(this.resizeTabs){
41354     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41355   //         this.autoSizeTabs();
41356 //        }else{
41357 //            item.autoSize();
41358        // }
41359     },
41360
41361     /**
41362      * Removes a {@link Roo.TabPanelItem}.
41363      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41364      */
41365     removeTab : function(id){
41366         var items = this.items;
41367         var tab = items[id];
41368         if(!tab) { return; }
41369         var index = items.indexOf(tab);
41370         if(this.active == tab && items.length > 1){
41371             var newTab = this.getNextAvailable(index);
41372             if(newTab) {
41373                 newTab.activate();
41374             }
41375         }
41376         this.stripEl.dom.removeChild(tab.pnode.dom);
41377         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41378             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41379         }
41380         items.splice(index, 1);
41381         delete this.items[tab.id];
41382         tab.fireEvent("close", tab);
41383         tab.purgeListeners();
41384         this.autoSizeTabs();
41385     },
41386
41387     getNextAvailable : function(start){
41388         var items = this.items;
41389         var index = start;
41390         // look for a next tab that will slide over to
41391         // replace the one being removed
41392         while(index < items.length){
41393             var item = items[++index];
41394             if(item && !item.isHidden()){
41395                 return item;
41396             }
41397         }
41398         // if one isn't found select the previous tab (on the left)
41399         index = start;
41400         while(index >= 0){
41401             var item = items[--index];
41402             if(item && !item.isHidden()){
41403                 return item;
41404             }
41405         }
41406         return null;
41407     },
41408
41409     /**
41410      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41411      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41412      */
41413     disableTab : function(id){
41414         var tab = this.items[id];
41415         if(tab && this.active != tab){
41416             tab.disable();
41417         }
41418     },
41419
41420     /**
41421      * Enables a {@link Roo.TabPanelItem} that is disabled.
41422      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41423      */
41424     enableTab : function(id){
41425         var tab = this.items[id];
41426         tab.enable();
41427     },
41428
41429     /**
41430      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41431      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41432      * @return {Roo.TabPanelItem} The TabPanelItem.
41433      */
41434     activate : function(id)
41435     {
41436         //Roo.log('activite:'  + id);
41437         
41438         var tab = this.items[id];
41439         if(!tab){
41440             return null;
41441         }
41442         if(tab == this.active || tab.disabled){
41443             return tab;
41444         }
41445         var e = {};
41446         this.fireEvent("beforetabchange", this, e, tab);
41447         if(e.cancel !== true && !tab.disabled){
41448             if(this.active){
41449                 this.active.hide();
41450             }
41451             this.active = this.items[id];
41452             this.active.show();
41453             this.fireEvent("tabchange", this, this.active);
41454         }
41455         return tab;
41456     },
41457
41458     /**
41459      * Gets the active {@link Roo.TabPanelItem}.
41460      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41461      */
41462     getActiveTab : function(){
41463         return this.active;
41464     },
41465
41466     /**
41467      * Updates the tab body element to fit the height of the container element
41468      * for overflow scrolling
41469      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41470      */
41471     syncHeight : function(targetHeight){
41472         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41473         var bm = this.bodyEl.getMargins();
41474         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41475         this.bodyEl.setHeight(newHeight);
41476         return newHeight;
41477     },
41478
41479     onResize : function(){
41480         if(this.monitorResize){
41481             this.autoSizeTabs();
41482         }
41483     },
41484
41485     /**
41486      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41487      */
41488     beginUpdate : function(){
41489         this.updating = true;
41490     },
41491
41492     /**
41493      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41494      */
41495     endUpdate : function(){
41496         this.updating = false;
41497         this.autoSizeTabs();
41498     },
41499
41500     /**
41501      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41502      */
41503     autoSizeTabs : function()
41504     {
41505         var count = this.items.length;
41506         var vcount = count - this.hiddenCount;
41507         
41508         if (vcount < 2) {
41509             this.stripEl.hide();
41510         } else {
41511             this.stripEl.show();
41512         }
41513         
41514         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41515             return;
41516         }
41517         
41518         
41519         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41520         var availWidth = Math.floor(w / vcount);
41521         var b = this.stripBody;
41522         if(b.getWidth() > w){
41523             var tabs = this.items;
41524             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41525             if(availWidth < this.minTabWidth){
41526                 /*if(!this.sleft){    // incomplete scrolling code
41527                     this.createScrollButtons();
41528                 }
41529                 this.showScroll();
41530                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41531             }
41532         }else{
41533             if(this.currentTabWidth < this.preferredTabWidth){
41534                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41535             }
41536         }
41537     },
41538
41539     /**
41540      * Returns the number of tabs in this TabPanel.
41541      * @return {Number}
41542      */
41543      getCount : function(){
41544          return this.items.length;
41545      },
41546
41547     /**
41548      * Resizes all the tabs to the passed width
41549      * @param {Number} The new width
41550      */
41551     setTabWidth : function(width){
41552         this.currentTabWidth = width;
41553         for(var i = 0, len = this.items.length; i < len; i++) {
41554                 if(!this.items[i].isHidden()) {
41555                 this.items[i].setWidth(width);
41556             }
41557         }
41558     },
41559
41560     /**
41561      * Destroys this TabPanel
41562      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41563      */
41564     destroy : function(removeEl){
41565         Roo.EventManager.removeResizeListener(this.onResize, this);
41566         for(var i = 0, len = this.items.length; i < len; i++){
41567             this.items[i].purgeListeners();
41568         }
41569         if(removeEl === true){
41570             this.el.update("");
41571             this.el.remove();
41572         }
41573     },
41574     
41575     createStrip : function(container)
41576     {
41577         var strip = document.createElement("nav");
41578         strip.className = Roo.bootstrap.version == 4 ?
41579             "navbar-light bg-light" : 
41580             "navbar navbar-default"; //"x-tabs-wrap";
41581         container.appendChild(strip);
41582         return strip;
41583     },
41584     
41585     createStripList : function(strip)
41586     {
41587         // div wrapper for retard IE
41588         // returns the "tr" element.
41589         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41590         //'<div class="x-tabs-strip-wrap">'+
41591           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41592           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41593         return strip.firstChild; //.firstChild.firstChild.firstChild;
41594     },
41595     createBody : function(container)
41596     {
41597         var body = document.createElement("div");
41598         Roo.id(body, "tab-body");
41599         //Roo.fly(body).addClass("x-tabs-body");
41600         Roo.fly(body).addClass("tab-content");
41601         container.appendChild(body);
41602         return body;
41603     },
41604     createItemBody :function(bodyEl, id){
41605         var body = Roo.getDom(id);
41606         if(!body){
41607             body = document.createElement("div");
41608             body.id = id;
41609         }
41610         //Roo.fly(body).addClass("x-tabs-item-body");
41611         Roo.fly(body).addClass("tab-pane");
41612          bodyEl.insertBefore(body, bodyEl.firstChild);
41613         return body;
41614     },
41615     /** @private */
41616     createStripElements :  function(stripEl, text, closable, tpl)
41617     {
41618         var td = document.createElement("li"); // was td..
41619         td.className = 'nav-item';
41620         
41621         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41622         
41623         
41624         stripEl.appendChild(td);
41625         /*if(closable){
41626             td.className = "x-tabs-closable";
41627             if(!this.closeTpl){
41628                 this.closeTpl = new Roo.Template(
41629                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41630                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41631                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41632                 );
41633             }
41634             var el = this.closeTpl.overwrite(td, {"text": text});
41635             var close = el.getElementsByTagName("div")[0];
41636             var inner = el.getElementsByTagName("em")[0];
41637             return {"el": el, "close": close, "inner": inner};
41638         } else {
41639         */
41640         // not sure what this is..
41641 //            if(!this.tabTpl){
41642                 //this.tabTpl = new Roo.Template(
41643                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41644                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41645                 //);
41646 //                this.tabTpl = new Roo.Template(
41647 //                   '<a href="#">' +
41648 //                   '<span unselectable="on"' +
41649 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41650 //                            ' >{text}</span></a>'
41651 //                );
41652 //                
41653 //            }
41654
41655
41656             var template = tpl || this.tabTpl || false;
41657             
41658             if(!template){
41659                 template =  new Roo.Template(
41660                         Roo.bootstrap.version == 4 ? 
41661                             (
41662                                 '<a class="nav-link" href="#" unselectable="on"' +
41663                                      (this.disableTooltips ? '' : ' title="{text}"') +
41664                                      ' >{text}</a>'
41665                             ) : (
41666                                 '<a class="nav-link" href="#">' +
41667                                 '<span unselectable="on"' +
41668                                          (this.disableTooltips ? '' : ' title="{text}"') +
41669                                     ' >{text}</span></a>'
41670                             )
41671                 );
41672             }
41673             
41674             switch (typeof(template)) {
41675                 case 'object' :
41676                     break;
41677                 case 'string' :
41678                     template = new Roo.Template(template);
41679                     break;
41680                 default :
41681                     break;
41682             }
41683             
41684             var el = template.overwrite(td, {"text": text});
41685             
41686             var inner = el.getElementsByTagName("span")[0];
41687             
41688             return {"el": el, "inner": inner};
41689             
41690     }
41691         
41692     
41693 });
41694
41695 /**
41696  * @class Roo.TabPanelItem
41697  * @extends Roo.util.Observable
41698  * Represents an individual item (tab plus body) in a TabPanel.
41699  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41700  * @param {String} id The id of this TabPanelItem
41701  * @param {String} text The text for the tab of this TabPanelItem
41702  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41703  */
41704 Roo.bootstrap.panel.TabItem = function(config){
41705     /**
41706      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41707      * @type Roo.TabPanel
41708      */
41709     this.tabPanel = config.panel;
41710     /**
41711      * The id for this TabPanelItem
41712      * @type String
41713      */
41714     this.id = config.id;
41715     /** @private */
41716     this.disabled = false;
41717     /** @private */
41718     this.text = config.text;
41719     /** @private */
41720     this.loaded = false;
41721     this.closable = config.closable;
41722
41723     /**
41724      * The body element for this TabPanelItem.
41725      * @type Roo.Element
41726      */
41727     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41728     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41729     this.bodyEl.setStyle("display", "block");
41730     this.bodyEl.setStyle("zoom", "1");
41731     //this.hideAction();
41732
41733     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41734     /** @private */
41735     this.el = Roo.get(els.el);
41736     this.inner = Roo.get(els.inner, true);
41737      this.textEl = Roo.bootstrap.version == 4 ?
41738         this.el : Roo.get(this.el.dom.firstChild, true);
41739
41740     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41741     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41742
41743     
41744 //    this.el.on("mousedown", this.onTabMouseDown, this);
41745     this.el.on("click", this.onTabClick, this);
41746     /** @private */
41747     if(config.closable){
41748         var c = Roo.get(els.close, true);
41749         c.dom.title = this.closeText;
41750         c.addClassOnOver("close-over");
41751         c.on("click", this.closeClick, this);
41752      }
41753
41754     this.addEvents({
41755          /**
41756          * @event activate
41757          * Fires when this tab becomes the active tab.
41758          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41759          * @param {Roo.TabPanelItem} this
41760          */
41761         "activate": true,
41762         /**
41763          * @event beforeclose
41764          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41765          * @param {Roo.TabPanelItem} this
41766          * @param {Object} e Set cancel to true on this object to cancel the close.
41767          */
41768         "beforeclose": true,
41769         /**
41770          * @event close
41771          * Fires when this tab is closed.
41772          * @param {Roo.TabPanelItem} this
41773          */
41774          "close": true,
41775         /**
41776          * @event deactivate
41777          * Fires when this tab is no longer the active tab.
41778          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41779          * @param {Roo.TabPanelItem} this
41780          */
41781          "deactivate" : true
41782     });
41783     this.hidden = false;
41784
41785     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41786 };
41787
41788 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41789            {
41790     purgeListeners : function(){
41791        Roo.util.Observable.prototype.purgeListeners.call(this);
41792        this.el.removeAllListeners();
41793     },
41794     /**
41795      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41796      */
41797     show : function(){
41798         this.status_node.addClass("active");
41799         this.showAction();
41800         if(Roo.isOpera){
41801             this.tabPanel.stripWrap.repaint();
41802         }
41803         this.fireEvent("activate", this.tabPanel, this);
41804     },
41805
41806     /**
41807      * Returns true if this tab is the active tab.
41808      * @return {Boolean}
41809      */
41810     isActive : function(){
41811         return this.tabPanel.getActiveTab() == this;
41812     },
41813
41814     /**
41815      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41816      */
41817     hide : function(){
41818         this.status_node.removeClass("active");
41819         this.hideAction();
41820         this.fireEvent("deactivate", this.tabPanel, this);
41821     },
41822
41823     hideAction : function(){
41824         this.bodyEl.hide();
41825         this.bodyEl.setStyle("position", "absolute");
41826         this.bodyEl.setLeft("-20000px");
41827         this.bodyEl.setTop("-20000px");
41828     },
41829
41830     showAction : function(){
41831         this.bodyEl.setStyle("position", "relative");
41832         this.bodyEl.setTop("");
41833         this.bodyEl.setLeft("");
41834         this.bodyEl.show();
41835     },
41836
41837     /**
41838      * Set the tooltip for the tab.
41839      * @param {String} tooltip The tab's tooltip
41840      */
41841     setTooltip : function(text){
41842         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41843             this.textEl.dom.qtip = text;
41844             this.textEl.dom.removeAttribute('title');
41845         }else{
41846             this.textEl.dom.title = text;
41847         }
41848     },
41849
41850     onTabClick : function(e){
41851         e.preventDefault();
41852         this.tabPanel.activate(this.id);
41853     },
41854
41855     onTabMouseDown : function(e){
41856         e.preventDefault();
41857         this.tabPanel.activate(this.id);
41858     },
41859 /*
41860     getWidth : function(){
41861         return this.inner.getWidth();
41862     },
41863
41864     setWidth : function(width){
41865         var iwidth = width - this.linode.getPadding("lr");
41866         this.inner.setWidth(iwidth);
41867         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41868         this.linode.setWidth(width);
41869     },
41870 */
41871     /**
41872      * Show or hide the tab
41873      * @param {Boolean} hidden True to hide or false to show.
41874      */
41875     setHidden : function(hidden){
41876         this.hidden = hidden;
41877         this.linode.setStyle("display", hidden ? "none" : "");
41878     },
41879
41880     /**
41881      * Returns true if this tab is "hidden"
41882      * @return {Boolean}
41883      */
41884     isHidden : function(){
41885         return this.hidden;
41886     },
41887
41888     /**
41889      * Returns the text for this tab
41890      * @return {String}
41891      */
41892     getText : function(){
41893         return this.text;
41894     },
41895     /*
41896     autoSize : function(){
41897         //this.el.beginMeasure();
41898         this.textEl.setWidth(1);
41899         /*
41900          *  #2804 [new] Tabs in Roojs
41901          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41902          */
41903         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41904         //this.el.endMeasure();
41905     //},
41906
41907     /**
41908      * Sets the text for the tab (Note: this also sets the tooltip text)
41909      * @param {String} text The tab's text and tooltip
41910      */
41911     setText : function(text){
41912         this.text = text;
41913         this.textEl.update(text);
41914         this.setTooltip(text);
41915         //if(!this.tabPanel.resizeTabs){
41916         //    this.autoSize();
41917         //}
41918     },
41919     /**
41920      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41921      */
41922     activate : function(){
41923         this.tabPanel.activate(this.id);
41924     },
41925
41926     /**
41927      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41928      */
41929     disable : function(){
41930         if(this.tabPanel.active != this){
41931             this.disabled = true;
41932             this.status_node.addClass("disabled");
41933         }
41934     },
41935
41936     /**
41937      * Enables this TabPanelItem if it was previously disabled.
41938      */
41939     enable : function(){
41940         this.disabled = false;
41941         this.status_node.removeClass("disabled");
41942     },
41943
41944     /**
41945      * Sets the content for this TabPanelItem.
41946      * @param {String} content The content
41947      * @param {Boolean} loadScripts true to look for and load scripts
41948      */
41949     setContent : function(content, loadScripts){
41950         this.bodyEl.update(content, loadScripts);
41951     },
41952
41953     /**
41954      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41955      * @return {Roo.UpdateManager} The UpdateManager
41956      */
41957     getUpdateManager : function(){
41958         return this.bodyEl.getUpdateManager();
41959     },
41960
41961     /**
41962      * Set a URL to be used to load the content for this TabPanelItem.
41963      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41964      * @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)
41965      * @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)
41966      * @return {Roo.UpdateManager} The UpdateManager
41967      */
41968     setUrl : function(url, params, loadOnce){
41969         if(this.refreshDelegate){
41970             this.un('activate', this.refreshDelegate);
41971         }
41972         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41973         this.on("activate", this.refreshDelegate);
41974         return this.bodyEl.getUpdateManager();
41975     },
41976
41977     /** @private */
41978     _handleRefresh : function(url, params, loadOnce){
41979         if(!loadOnce || !this.loaded){
41980             var updater = this.bodyEl.getUpdateManager();
41981             updater.update(url, params, this._setLoaded.createDelegate(this));
41982         }
41983     },
41984
41985     /**
41986      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41987      *   Will fail silently if the setUrl method has not been called.
41988      *   This does not activate the panel, just updates its content.
41989      */
41990     refresh : function(){
41991         if(this.refreshDelegate){
41992            this.loaded = false;
41993            this.refreshDelegate();
41994         }
41995     },
41996
41997     /** @private */
41998     _setLoaded : function(){
41999         this.loaded = true;
42000     },
42001
42002     /** @private */
42003     closeClick : function(e){
42004         var o = {};
42005         e.stopEvent();
42006         this.fireEvent("beforeclose", this, o);
42007         if(o.cancel !== true){
42008             this.tabPanel.removeTab(this.id);
42009         }
42010     },
42011     /**
42012      * The text displayed in the tooltip for the close icon.
42013      * @type String
42014      */
42015     closeText : "Close this tab"
42016 });
42017 /**
42018 *    This script refer to:
42019 *    Title: International Telephone Input
42020 *    Author: Jack O'Connor
42021 *    Code version:  v12.1.12
42022 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42023 **/
42024
42025 Roo.bootstrap.form.PhoneInputData = function() {
42026     var d = [
42027       [
42028         "Afghanistan (‫افغانستان‬‎)",
42029         "af",
42030         "93"
42031       ],
42032       [
42033         "Albania (Shqipëri)",
42034         "al",
42035         "355"
42036       ],
42037       [
42038         "Algeria (‫الجزائر‬‎)",
42039         "dz",
42040         "213"
42041       ],
42042       [
42043         "American Samoa",
42044         "as",
42045         "1684"
42046       ],
42047       [
42048         "Andorra",
42049         "ad",
42050         "376"
42051       ],
42052       [
42053         "Angola",
42054         "ao",
42055         "244"
42056       ],
42057       [
42058         "Anguilla",
42059         "ai",
42060         "1264"
42061       ],
42062       [
42063         "Antigua and Barbuda",
42064         "ag",
42065         "1268"
42066       ],
42067       [
42068         "Argentina",
42069         "ar",
42070         "54"
42071       ],
42072       [
42073         "Armenia (Հայաստան)",
42074         "am",
42075         "374"
42076       ],
42077       [
42078         "Aruba",
42079         "aw",
42080         "297"
42081       ],
42082       [
42083         "Australia",
42084         "au",
42085         "61",
42086         0
42087       ],
42088       [
42089         "Austria (Österreich)",
42090         "at",
42091         "43"
42092       ],
42093       [
42094         "Azerbaijan (Azərbaycan)",
42095         "az",
42096         "994"
42097       ],
42098       [
42099         "Bahamas",
42100         "bs",
42101         "1242"
42102       ],
42103       [
42104         "Bahrain (‫البحرين‬‎)",
42105         "bh",
42106         "973"
42107       ],
42108       [
42109         "Bangladesh (বাংলাদেশ)",
42110         "bd",
42111         "880"
42112       ],
42113       [
42114         "Barbados",
42115         "bb",
42116         "1246"
42117       ],
42118       [
42119         "Belarus (Беларусь)",
42120         "by",
42121         "375"
42122       ],
42123       [
42124         "Belgium (België)",
42125         "be",
42126         "32"
42127       ],
42128       [
42129         "Belize",
42130         "bz",
42131         "501"
42132       ],
42133       [
42134         "Benin (Bénin)",
42135         "bj",
42136         "229"
42137       ],
42138       [
42139         "Bermuda",
42140         "bm",
42141         "1441"
42142       ],
42143       [
42144         "Bhutan (འབྲུག)",
42145         "bt",
42146         "975"
42147       ],
42148       [
42149         "Bolivia",
42150         "bo",
42151         "591"
42152       ],
42153       [
42154         "Bosnia and Herzegovina (Босна и Херцеговина)",
42155         "ba",
42156         "387"
42157       ],
42158       [
42159         "Botswana",
42160         "bw",
42161         "267"
42162       ],
42163       [
42164         "Brazil (Brasil)",
42165         "br",
42166         "55"
42167       ],
42168       [
42169         "British Indian Ocean Territory",
42170         "io",
42171         "246"
42172       ],
42173       [
42174         "British Virgin Islands",
42175         "vg",
42176         "1284"
42177       ],
42178       [
42179         "Brunei",
42180         "bn",
42181         "673"
42182       ],
42183       [
42184         "Bulgaria (България)",
42185         "bg",
42186         "359"
42187       ],
42188       [
42189         "Burkina Faso",
42190         "bf",
42191         "226"
42192       ],
42193       [
42194         "Burundi (Uburundi)",
42195         "bi",
42196         "257"
42197       ],
42198       [
42199         "Cambodia (កម្ពុជា)",
42200         "kh",
42201         "855"
42202       ],
42203       [
42204         "Cameroon (Cameroun)",
42205         "cm",
42206         "237"
42207       ],
42208       [
42209         "Canada",
42210         "ca",
42211         "1",
42212         1,
42213         ["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"]
42214       ],
42215       [
42216         "Cape Verde (Kabu Verdi)",
42217         "cv",
42218         "238"
42219       ],
42220       [
42221         "Caribbean Netherlands",
42222         "bq",
42223         "599",
42224         1
42225       ],
42226       [
42227         "Cayman Islands",
42228         "ky",
42229         "1345"
42230       ],
42231       [
42232         "Central African Republic (République centrafricaine)",
42233         "cf",
42234         "236"
42235       ],
42236       [
42237         "Chad (Tchad)",
42238         "td",
42239         "235"
42240       ],
42241       [
42242         "Chile",
42243         "cl",
42244         "56"
42245       ],
42246       [
42247         "China (中国)",
42248         "cn",
42249         "86"
42250       ],
42251       [
42252         "Christmas Island",
42253         "cx",
42254         "61",
42255         2
42256       ],
42257       [
42258         "Cocos (Keeling) Islands",
42259         "cc",
42260         "61",
42261         1
42262       ],
42263       [
42264         "Colombia",
42265         "co",
42266         "57"
42267       ],
42268       [
42269         "Comoros (‫جزر القمر‬‎)",
42270         "km",
42271         "269"
42272       ],
42273       [
42274         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42275         "cd",
42276         "243"
42277       ],
42278       [
42279         "Congo (Republic) (Congo-Brazzaville)",
42280         "cg",
42281         "242"
42282       ],
42283       [
42284         "Cook Islands",
42285         "ck",
42286         "682"
42287       ],
42288       [
42289         "Costa Rica",
42290         "cr",
42291         "506"
42292       ],
42293       [
42294         "Côte d’Ivoire",
42295         "ci",
42296         "225"
42297       ],
42298       [
42299         "Croatia (Hrvatska)",
42300         "hr",
42301         "385"
42302       ],
42303       [
42304         "Cuba",
42305         "cu",
42306         "53"
42307       ],
42308       [
42309         "Curaçao",
42310         "cw",
42311         "599",
42312         0
42313       ],
42314       [
42315         "Cyprus (Κύπρος)",
42316         "cy",
42317         "357"
42318       ],
42319       [
42320         "Czech Republic (Česká republika)",
42321         "cz",
42322         "420"
42323       ],
42324       [
42325         "Denmark (Danmark)",
42326         "dk",
42327         "45"
42328       ],
42329       [
42330         "Djibouti",
42331         "dj",
42332         "253"
42333       ],
42334       [
42335         "Dominica",
42336         "dm",
42337         "1767"
42338       ],
42339       [
42340         "Dominican Republic (República Dominicana)",
42341         "do",
42342         "1",
42343         2,
42344         ["809", "829", "849"]
42345       ],
42346       [
42347         "Ecuador",
42348         "ec",
42349         "593"
42350       ],
42351       [
42352         "Egypt (‫مصر‬‎)",
42353         "eg",
42354         "20"
42355       ],
42356       [
42357         "El Salvador",
42358         "sv",
42359         "503"
42360       ],
42361       [
42362         "Equatorial Guinea (Guinea Ecuatorial)",
42363         "gq",
42364         "240"
42365       ],
42366       [
42367         "Eritrea",
42368         "er",
42369         "291"
42370       ],
42371       [
42372         "Estonia (Eesti)",
42373         "ee",
42374         "372"
42375       ],
42376       [
42377         "Ethiopia",
42378         "et",
42379         "251"
42380       ],
42381       [
42382         "Falkland Islands (Islas Malvinas)",
42383         "fk",
42384         "500"
42385       ],
42386       [
42387         "Faroe Islands (Føroyar)",
42388         "fo",
42389         "298"
42390       ],
42391       [
42392         "Fiji",
42393         "fj",
42394         "679"
42395       ],
42396       [
42397         "Finland (Suomi)",
42398         "fi",
42399         "358",
42400         0
42401       ],
42402       [
42403         "France",
42404         "fr",
42405         "33"
42406       ],
42407       [
42408         "French Guiana (Guyane française)",
42409         "gf",
42410         "594"
42411       ],
42412       [
42413         "French Polynesia (Polynésie française)",
42414         "pf",
42415         "689"
42416       ],
42417       [
42418         "Gabon",
42419         "ga",
42420         "241"
42421       ],
42422       [
42423         "Gambia",
42424         "gm",
42425         "220"
42426       ],
42427       [
42428         "Georgia (საქართველო)",
42429         "ge",
42430         "995"
42431       ],
42432       [
42433         "Germany (Deutschland)",
42434         "de",
42435         "49"
42436       ],
42437       [
42438         "Ghana (Gaana)",
42439         "gh",
42440         "233"
42441       ],
42442       [
42443         "Gibraltar",
42444         "gi",
42445         "350"
42446       ],
42447       [
42448         "Greece (Ελλάδα)",
42449         "gr",
42450         "30"
42451       ],
42452       [
42453         "Greenland (Kalaallit Nunaat)",
42454         "gl",
42455         "299"
42456       ],
42457       [
42458         "Grenada",
42459         "gd",
42460         "1473"
42461       ],
42462       [
42463         "Guadeloupe",
42464         "gp",
42465         "590",
42466         0
42467       ],
42468       [
42469         "Guam",
42470         "gu",
42471         "1671"
42472       ],
42473       [
42474         "Guatemala",
42475         "gt",
42476         "502"
42477       ],
42478       [
42479         "Guernsey",
42480         "gg",
42481         "44",
42482         1
42483       ],
42484       [
42485         "Guinea (Guinée)",
42486         "gn",
42487         "224"
42488       ],
42489       [
42490         "Guinea-Bissau (Guiné Bissau)",
42491         "gw",
42492         "245"
42493       ],
42494       [
42495         "Guyana",
42496         "gy",
42497         "592"
42498       ],
42499       [
42500         "Haiti",
42501         "ht",
42502         "509"
42503       ],
42504       [
42505         "Honduras",
42506         "hn",
42507         "504"
42508       ],
42509       [
42510         "Hong Kong (香港)",
42511         "hk",
42512         "852"
42513       ],
42514       [
42515         "Hungary (Magyarország)",
42516         "hu",
42517         "36"
42518       ],
42519       [
42520         "Iceland (Ísland)",
42521         "is",
42522         "354"
42523       ],
42524       [
42525         "India (भारत)",
42526         "in",
42527         "91"
42528       ],
42529       [
42530         "Indonesia",
42531         "id",
42532         "62"
42533       ],
42534       [
42535         "Iran (‫ایران‬‎)",
42536         "ir",
42537         "98"
42538       ],
42539       [
42540         "Iraq (‫العراق‬‎)",
42541         "iq",
42542         "964"
42543       ],
42544       [
42545         "Ireland",
42546         "ie",
42547         "353"
42548       ],
42549       [
42550         "Isle of Man",
42551         "im",
42552         "44",
42553         2
42554       ],
42555       [
42556         "Israel (‫ישראל‬‎)",
42557         "il",
42558         "972"
42559       ],
42560       [
42561         "Italy (Italia)",
42562         "it",
42563         "39",
42564         0
42565       ],
42566       [
42567         "Jamaica",
42568         "jm",
42569         "1876"
42570       ],
42571       [
42572         "Japan (日本)",
42573         "jp",
42574         "81"
42575       ],
42576       [
42577         "Jersey",
42578         "je",
42579         "44",
42580         3
42581       ],
42582       [
42583         "Jordan (‫الأردن‬‎)",
42584         "jo",
42585         "962"
42586       ],
42587       [
42588         "Kazakhstan (Казахстан)",
42589         "kz",
42590         "7",
42591         1
42592       ],
42593       [
42594         "Kenya",
42595         "ke",
42596         "254"
42597       ],
42598       [
42599         "Kiribati",
42600         "ki",
42601         "686"
42602       ],
42603       [
42604         "Kosovo",
42605         "xk",
42606         "383"
42607       ],
42608       [
42609         "Kuwait (‫الكويت‬‎)",
42610         "kw",
42611         "965"
42612       ],
42613       [
42614         "Kyrgyzstan (Кыргызстан)",
42615         "kg",
42616         "996"
42617       ],
42618       [
42619         "Laos (ລາວ)",
42620         "la",
42621         "856"
42622       ],
42623       [
42624         "Latvia (Latvija)",
42625         "lv",
42626         "371"
42627       ],
42628       [
42629         "Lebanon (‫لبنان‬‎)",
42630         "lb",
42631         "961"
42632       ],
42633       [
42634         "Lesotho",
42635         "ls",
42636         "266"
42637       ],
42638       [
42639         "Liberia",
42640         "lr",
42641         "231"
42642       ],
42643       [
42644         "Libya (‫ليبيا‬‎)",
42645         "ly",
42646         "218"
42647       ],
42648       [
42649         "Liechtenstein",
42650         "li",
42651         "423"
42652       ],
42653       [
42654         "Lithuania (Lietuva)",
42655         "lt",
42656         "370"
42657       ],
42658       [
42659         "Luxembourg",
42660         "lu",
42661         "352"
42662       ],
42663       [
42664         "Macau (澳門)",
42665         "mo",
42666         "853"
42667       ],
42668       [
42669         "Macedonia (FYROM) (Македонија)",
42670         "mk",
42671         "389"
42672       ],
42673       [
42674         "Madagascar (Madagasikara)",
42675         "mg",
42676         "261"
42677       ],
42678       [
42679         "Malawi",
42680         "mw",
42681         "265"
42682       ],
42683       [
42684         "Malaysia",
42685         "my",
42686         "60"
42687       ],
42688       [
42689         "Maldives",
42690         "mv",
42691         "960"
42692       ],
42693       [
42694         "Mali",
42695         "ml",
42696         "223"
42697       ],
42698       [
42699         "Malta",
42700         "mt",
42701         "356"
42702       ],
42703       [
42704         "Marshall Islands",
42705         "mh",
42706         "692"
42707       ],
42708       [
42709         "Martinique",
42710         "mq",
42711         "596"
42712       ],
42713       [
42714         "Mauritania (‫موريتانيا‬‎)",
42715         "mr",
42716         "222"
42717       ],
42718       [
42719         "Mauritius (Moris)",
42720         "mu",
42721         "230"
42722       ],
42723       [
42724         "Mayotte",
42725         "yt",
42726         "262",
42727         1
42728       ],
42729       [
42730         "Mexico (México)",
42731         "mx",
42732         "52"
42733       ],
42734       [
42735         "Micronesia",
42736         "fm",
42737         "691"
42738       ],
42739       [
42740         "Moldova (Republica Moldova)",
42741         "md",
42742         "373"
42743       ],
42744       [
42745         "Monaco",
42746         "mc",
42747         "377"
42748       ],
42749       [
42750         "Mongolia (Монгол)",
42751         "mn",
42752         "976"
42753       ],
42754       [
42755         "Montenegro (Crna Gora)",
42756         "me",
42757         "382"
42758       ],
42759       [
42760         "Montserrat",
42761         "ms",
42762         "1664"
42763       ],
42764       [
42765         "Morocco (‫المغرب‬‎)",
42766         "ma",
42767         "212",
42768         0
42769       ],
42770       [
42771         "Mozambique (Moçambique)",
42772         "mz",
42773         "258"
42774       ],
42775       [
42776         "Myanmar (Burma) (မြန်မာ)",
42777         "mm",
42778         "95"
42779       ],
42780       [
42781         "Namibia (Namibië)",
42782         "na",
42783         "264"
42784       ],
42785       [
42786         "Nauru",
42787         "nr",
42788         "674"
42789       ],
42790       [
42791         "Nepal (नेपाल)",
42792         "np",
42793         "977"
42794       ],
42795       [
42796         "Netherlands (Nederland)",
42797         "nl",
42798         "31"
42799       ],
42800       [
42801         "New Caledonia (Nouvelle-Calédonie)",
42802         "nc",
42803         "687"
42804       ],
42805       [
42806         "New Zealand",
42807         "nz",
42808         "64"
42809       ],
42810       [
42811         "Nicaragua",
42812         "ni",
42813         "505"
42814       ],
42815       [
42816         "Niger (Nijar)",
42817         "ne",
42818         "227"
42819       ],
42820       [
42821         "Nigeria",
42822         "ng",
42823         "234"
42824       ],
42825       [
42826         "Niue",
42827         "nu",
42828         "683"
42829       ],
42830       [
42831         "Norfolk Island",
42832         "nf",
42833         "672"
42834       ],
42835       [
42836         "North Korea (조선 민주주의 인민 공화국)",
42837         "kp",
42838         "850"
42839       ],
42840       [
42841         "Northern Mariana Islands",
42842         "mp",
42843         "1670"
42844       ],
42845       [
42846         "Norway (Norge)",
42847         "no",
42848         "47",
42849         0
42850       ],
42851       [
42852         "Oman (‫عُمان‬‎)",
42853         "om",
42854         "968"
42855       ],
42856       [
42857         "Pakistan (‫پاکستان‬‎)",
42858         "pk",
42859         "92"
42860       ],
42861       [
42862         "Palau",
42863         "pw",
42864         "680"
42865       ],
42866       [
42867         "Palestine (‫فلسطين‬‎)",
42868         "ps",
42869         "970"
42870       ],
42871       [
42872         "Panama (Panamá)",
42873         "pa",
42874         "507"
42875       ],
42876       [
42877         "Papua New Guinea",
42878         "pg",
42879         "675"
42880       ],
42881       [
42882         "Paraguay",
42883         "py",
42884         "595"
42885       ],
42886       [
42887         "Peru (Perú)",
42888         "pe",
42889         "51"
42890       ],
42891       [
42892         "Philippines",
42893         "ph",
42894         "63"
42895       ],
42896       [
42897         "Poland (Polska)",
42898         "pl",
42899         "48"
42900       ],
42901       [
42902         "Portugal",
42903         "pt",
42904         "351"
42905       ],
42906       [
42907         "Puerto Rico",
42908         "pr",
42909         "1",
42910         3,
42911         ["787", "939"]
42912       ],
42913       [
42914         "Qatar (‫قطر‬‎)",
42915         "qa",
42916         "974"
42917       ],
42918       [
42919         "Réunion (La Réunion)",
42920         "re",
42921         "262",
42922         0
42923       ],
42924       [
42925         "Romania (România)",
42926         "ro",
42927         "40"
42928       ],
42929       [
42930         "Russia (Россия)",
42931         "ru",
42932         "7",
42933         0
42934       ],
42935       [
42936         "Rwanda",
42937         "rw",
42938         "250"
42939       ],
42940       [
42941         "Saint Barthélemy",
42942         "bl",
42943         "590",
42944         1
42945       ],
42946       [
42947         "Saint Helena",
42948         "sh",
42949         "290"
42950       ],
42951       [
42952         "Saint Kitts and Nevis",
42953         "kn",
42954         "1869"
42955       ],
42956       [
42957         "Saint Lucia",
42958         "lc",
42959         "1758"
42960       ],
42961       [
42962         "Saint Martin (Saint-Martin (partie française))",
42963         "mf",
42964         "590",
42965         2
42966       ],
42967       [
42968         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42969         "pm",
42970         "508"
42971       ],
42972       [
42973         "Saint Vincent and the Grenadines",
42974         "vc",
42975         "1784"
42976       ],
42977       [
42978         "Samoa",
42979         "ws",
42980         "685"
42981       ],
42982       [
42983         "San Marino",
42984         "sm",
42985         "378"
42986       ],
42987       [
42988         "São Tomé and Príncipe (São Tomé e Príncipe)",
42989         "st",
42990         "239"
42991       ],
42992       [
42993         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42994         "sa",
42995         "966"
42996       ],
42997       [
42998         "Senegal (Sénégal)",
42999         "sn",
43000         "221"
43001       ],
43002       [
43003         "Serbia (Србија)",
43004         "rs",
43005         "381"
43006       ],
43007       [
43008         "Seychelles",
43009         "sc",
43010         "248"
43011       ],
43012       [
43013         "Sierra Leone",
43014         "sl",
43015         "232"
43016       ],
43017       [
43018         "Singapore",
43019         "sg",
43020         "65"
43021       ],
43022       [
43023         "Sint Maarten",
43024         "sx",
43025         "1721"
43026       ],
43027       [
43028         "Slovakia (Slovensko)",
43029         "sk",
43030         "421"
43031       ],
43032       [
43033         "Slovenia (Slovenija)",
43034         "si",
43035         "386"
43036       ],
43037       [
43038         "Solomon Islands",
43039         "sb",
43040         "677"
43041       ],
43042       [
43043         "Somalia (Soomaaliya)",
43044         "so",
43045         "252"
43046       ],
43047       [
43048         "South Africa",
43049         "za",
43050         "27"
43051       ],
43052       [
43053         "South Korea (대한민국)",
43054         "kr",
43055         "82"
43056       ],
43057       [
43058         "South Sudan (‫جنوب السودان‬‎)",
43059         "ss",
43060         "211"
43061       ],
43062       [
43063         "Spain (España)",
43064         "es",
43065         "34"
43066       ],
43067       [
43068         "Sri Lanka (ශ්‍රී ලංකාව)",
43069         "lk",
43070         "94"
43071       ],
43072       [
43073         "Sudan (‫السودان‬‎)",
43074         "sd",
43075         "249"
43076       ],
43077       [
43078         "Suriname",
43079         "sr",
43080         "597"
43081       ],
43082       [
43083         "Svalbard and Jan Mayen",
43084         "sj",
43085         "47",
43086         1
43087       ],
43088       [
43089         "Swaziland",
43090         "sz",
43091         "268"
43092       ],
43093       [
43094         "Sweden (Sverige)",
43095         "se",
43096         "46"
43097       ],
43098       [
43099         "Switzerland (Schweiz)",
43100         "ch",
43101         "41"
43102       ],
43103       [
43104         "Syria (‫سوريا‬‎)",
43105         "sy",
43106         "963"
43107       ],
43108       [
43109         "Taiwan (台灣)",
43110         "tw",
43111         "886"
43112       ],
43113       [
43114         "Tajikistan",
43115         "tj",
43116         "992"
43117       ],
43118       [
43119         "Tanzania",
43120         "tz",
43121         "255"
43122       ],
43123       [
43124         "Thailand (ไทย)",
43125         "th",
43126         "66"
43127       ],
43128       [
43129         "Timor-Leste",
43130         "tl",
43131         "670"
43132       ],
43133       [
43134         "Togo",
43135         "tg",
43136         "228"
43137       ],
43138       [
43139         "Tokelau",
43140         "tk",
43141         "690"
43142       ],
43143       [
43144         "Tonga",
43145         "to",
43146         "676"
43147       ],
43148       [
43149         "Trinidad and Tobago",
43150         "tt",
43151         "1868"
43152       ],
43153       [
43154         "Tunisia (‫تونس‬‎)",
43155         "tn",
43156         "216"
43157       ],
43158       [
43159         "Turkey (Türkiye)",
43160         "tr",
43161         "90"
43162       ],
43163       [
43164         "Turkmenistan",
43165         "tm",
43166         "993"
43167       ],
43168       [
43169         "Turks and Caicos Islands",
43170         "tc",
43171         "1649"
43172       ],
43173       [
43174         "Tuvalu",
43175         "tv",
43176         "688"
43177       ],
43178       [
43179         "U.S. Virgin Islands",
43180         "vi",
43181         "1340"
43182       ],
43183       [
43184         "Uganda",
43185         "ug",
43186         "256"
43187       ],
43188       [
43189         "Ukraine (Україна)",
43190         "ua",
43191         "380"
43192       ],
43193       [
43194         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43195         "ae",
43196         "971"
43197       ],
43198       [
43199         "United Kingdom",
43200         "gb",
43201         "44",
43202         0
43203       ],
43204       [
43205         "United States",
43206         "us",
43207         "1",
43208         0
43209       ],
43210       [
43211         "Uruguay",
43212         "uy",
43213         "598"
43214       ],
43215       [
43216         "Uzbekistan (Oʻzbekiston)",
43217         "uz",
43218         "998"
43219       ],
43220       [
43221         "Vanuatu",
43222         "vu",
43223         "678"
43224       ],
43225       [
43226         "Vatican City (Città del Vaticano)",
43227         "va",
43228         "39",
43229         1
43230       ],
43231       [
43232         "Venezuela",
43233         "ve",
43234         "58"
43235       ],
43236       [
43237         "Vietnam (Việt Nam)",
43238         "vn",
43239         "84"
43240       ],
43241       [
43242         "Wallis and Futuna (Wallis-et-Futuna)",
43243         "wf",
43244         "681"
43245       ],
43246       [
43247         "Western Sahara (‫الصحراء الغربية‬‎)",
43248         "eh",
43249         "212",
43250         1
43251       ],
43252       [
43253         "Yemen (‫اليمن‬‎)",
43254         "ye",
43255         "967"
43256       ],
43257       [
43258         "Zambia",
43259         "zm",
43260         "260"
43261       ],
43262       [
43263         "Zimbabwe",
43264         "zw",
43265         "263"
43266       ],
43267       [
43268         "Åland Islands",
43269         "ax",
43270         "358",
43271         1
43272       ]
43273   ];
43274   
43275   return d;
43276 }/**
43277 *    This script refer to:
43278 *    Title: International Telephone Input
43279 *    Author: Jack O'Connor
43280 *    Code version:  v12.1.12
43281 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43282 **/
43283
43284 /**
43285  * @class Roo.bootstrap.form.PhoneInput
43286  * @extends Roo.bootstrap.form.TriggerField
43287  * An input with International dial-code selection
43288  
43289  * @cfg {String} defaultDialCode default '+852'
43290  * @cfg {Array} preferedCountries default []
43291   
43292  * @constructor
43293  * Create a new PhoneInput.
43294  * @param {Object} config Configuration options
43295  */
43296
43297 Roo.bootstrap.form.PhoneInput = function(config) {
43298     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43299 };
43300
43301 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43302         /**
43303         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43304         */
43305         listWidth: undefined,
43306         
43307         selectedClass: 'active',
43308         
43309         invalidClass : "has-warning",
43310         
43311         validClass: 'has-success',
43312         
43313         allowed: '0123456789',
43314         
43315         max_length: 15,
43316         
43317         /**
43318          * @cfg {String} defaultDialCode The default dial code when initializing the input
43319          */
43320         defaultDialCode: '+852',
43321         
43322         /**
43323          * @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
43324          */
43325         preferedCountries: false,
43326         
43327         getAutoCreate : function()
43328         {
43329             var data = Roo.bootstrap.form.PhoneInputData();
43330             var align = this.labelAlign || this.parentLabelAlign();
43331             var id = Roo.id();
43332             
43333             this.allCountries = [];
43334             this.dialCodeMapping = [];
43335             
43336             for (var i = 0; i < data.length; i++) {
43337               var c = data[i];
43338               this.allCountries[i] = {
43339                 name: c[0],
43340                 iso2: c[1],
43341                 dialCode: c[2],
43342                 priority: c[3] || 0,
43343                 areaCodes: c[4] || null
43344               };
43345               this.dialCodeMapping[c[2]] = {
43346                   name: c[0],
43347                   iso2: c[1],
43348                   priority: c[3] || 0,
43349                   areaCodes: c[4] || null
43350               };
43351             }
43352             
43353             var cfg = {
43354                 cls: 'form-group',
43355                 cn: []
43356             };
43357             
43358             var input =  {
43359                 tag: 'input',
43360                 id : id,
43361                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43362                 maxlength: this.max_length,
43363                 cls : 'form-control tel-input',
43364                 autocomplete: 'new-password'
43365             };
43366             
43367             var hiddenInput = {
43368                 tag: 'input',
43369                 type: 'hidden',
43370                 cls: 'hidden-tel-input'
43371             };
43372             
43373             if (this.name) {
43374                 hiddenInput.name = this.name;
43375             }
43376             
43377             if (this.disabled) {
43378                 input.disabled = true;
43379             }
43380             
43381             var flag_container = {
43382                 tag: 'div',
43383                 cls: 'flag-box',
43384                 cn: [
43385                     {
43386                         tag: 'div',
43387                         cls: 'flag'
43388                     },
43389                     {
43390                         tag: 'div',
43391                         cls: 'caret'
43392                     }
43393                 ]
43394             };
43395             
43396             var box = {
43397                 tag: 'div',
43398                 cls: this.hasFeedback ? 'has-feedback' : '',
43399                 cn: [
43400                     hiddenInput,
43401                     input,
43402                     {
43403                         tag: 'input',
43404                         cls: 'dial-code-holder',
43405                         disabled: true
43406                     }
43407                 ]
43408             };
43409             
43410             var container = {
43411                 cls: 'roo-select2-container input-group',
43412                 cn: [
43413                     flag_container,
43414                     box
43415                 ]
43416             };
43417             
43418             if (this.fieldLabel.length) {
43419                 var indicator = {
43420                     tag: 'i',
43421                     tooltip: 'This field is required'
43422                 };
43423                 
43424                 var label = {
43425                     tag: 'label',
43426                     'for':  id,
43427                     cls: 'control-label',
43428                     cn: []
43429                 };
43430                 
43431                 var label_text = {
43432                     tag: 'span',
43433                     html: this.fieldLabel
43434                 };
43435                 
43436                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43437                 label.cn = [
43438                     indicator,
43439                     label_text
43440                 ];
43441                 
43442                 if(this.indicatorpos == 'right') {
43443                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43444                     label.cn = [
43445                         label_text,
43446                         indicator
43447                     ];
43448                 }
43449                 
43450                 if(align == 'left') {
43451                     container = {
43452                         tag: 'div',
43453                         cn: [
43454                             container
43455                         ]
43456                     };
43457                     
43458                     if(this.labelWidth > 12){
43459                         label.style = "width: " + this.labelWidth + 'px';
43460                     }
43461                     if(this.labelWidth < 13 && this.labelmd == 0){
43462                         this.labelmd = this.labelWidth;
43463                     }
43464                     if(this.labellg > 0){
43465                         label.cls += ' col-lg-' + this.labellg;
43466                         input.cls += ' col-lg-' + (12 - this.labellg);
43467                     }
43468                     if(this.labelmd > 0){
43469                         label.cls += ' col-md-' + this.labelmd;
43470                         container.cls += ' col-md-' + (12 - this.labelmd);
43471                     }
43472                     if(this.labelsm > 0){
43473                         label.cls += ' col-sm-' + this.labelsm;
43474                         container.cls += ' col-sm-' + (12 - this.labelsm);
43475                     }
43476                     if(this.labelxs > 0){
43477                         label.cls += ' col-xs-' + this.labelxs;
43478                         container.cls += ' col-xs-' + (12 - this.labelxs);
43479                     }
43480                 }
43481             }
43482             
43483             cfg.cn = [
43484                 label,
43485                 container
43486             ];
43487             
43488             var settings = this;
43489             
43490             ['xs','sm','md','lg'].map(function(size){
43491                 if (settings[size]) {
43492                     cfg.cls += ' col-' + size + '-' + settings[size];
43493                 }
43494             });
43495             
43496             this.store = new Roo.data.Store({
43497                 proxy : new Roo.data.MemoryProxy({}),
43498                 reader : new Roo.data.JsonReader({
43499                     fields : [
43500                         {
43501                             'name' : 'name',
43502                             'type' : 'string'
43503                         },
43504                         {
43505                             'name' : 'iso2',
43506                             'type' : 'string'
43507                         },
43508                         {
43509                             'name' : 'dialCode',
43510                             'type' : 'string'
43511                         },
43512                         {
43513                             'name' : 'priority',
43514                             'type' : 'string'
43515                         },
43516                         {
43517                             'name' : 'areaCodes',
43518                             'type' : 'string'
43519                         }
43520                     ]
43521                 })
43522             });
43523             
43524             if(!this.preferedCountries) {
43525                 this.preferedCountries = [
43526                     'hk',
43527                     'gb',
43528                     'us'
43529                 ];
43530             }
43531             
43532             var p = this.preferedCountries.reverse();
43533             
43534             if(p) {
43535                 for (var i = 0; i < p.length; i++) {
43536                     for (var j = 0; j < this.allCountries.length; j++) {
43537                         if(this.allCountries[j].iso2 == p[i]) {
43538                             var t = this.allCountries[j];
43539                             this.allCountries.splice(j,1);
43540                             this.allCountries.unshift(t);
43541                         }
43542                     } 
43543                 }
43544             }
43545             
43546             this.store.proxy.data = {
43547                 success: true,
43548                 data: this.allCountries
43549             };
43550             
43551             return cfg;
43552         },
43553         
43554         initEvents : function()
43555         {
43556             this.createList();
43557             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43558             
43559             this.indicator = this.indicatorEl();
43560             this.flag = this.flagEl();
43561             this.dialCodeHolder = this.dialCodeHolderEl();
43562             
43563             this.trigger = this.el.select('div.flag-box',true).first();
43564             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43565             
43566             var _this = this;
43567             
43568             (function(){
43569                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43570                 _this.list.setWidth(lw);
43571             }).defer(100);
43572             
43573             this.list.on('mouseover', this.onViewOver, this);
43574             this.list.on('mousemove', this.onViewMove, this);
43575             this.inputEl().on("keyup", this.onKeyUp, this);
43576             this.inputEl().on("keypress", this.onKeyPress, this);
43577             
43578             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43579
43580             this.view = new Roo.View(this.list, this.tpl, {
43581                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43582             });
43583             
43584             this.view.on('click', this.onViewClick, this);
43585             this.setValue(this.defaultDialCode);
43586         },
43587         
43588         onTriggerClick : function(e)
43589         {
43590             Roo.log('trigger click');
43591             if(this.disabled){
43592                 return;
43593             }
43594             
43595             if(this.isExpanded()){
43596                 this.collapse();
43597                 this.hasFocus = false;
43598             }else {
43599                 this.store.load({});
43600                 this.hasFocus = true;
43601                 this.expand();
43602             }
43603         },
43604         
43605         isExpanded : function()
43606         {
43607             return this.list.isVisible();
43608         },
43609         
43610         collapse : function()
43611         {
43612             if(!this.isExpanded()){
43613                 return;
43614             }
43615             this.list.hide();
43616             Roo.get(document).un('mousedown', this.collapseIf, this);
43617             Roo.get(document).un('mousewheel', this.collapseIf, this);
43618             this.fireEvent('collapse', this);
43619             this.validate();
43620         },
43621         
43622         expand : function()
43623         {
43624             Roo.log('expand');
43625
43626             if(this.isExpanded() || !this.hasFocus){
43627                 return;
43628             }
43629             
43630             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43631             this.list.setWidth(lw);
43632             
43633             this.list.show();
43634             this.restrictHeight();
43635             
43636             Roo.get(document).on('mousedown', this.collapseIf, this);
43637             Roo.get(document).on('mousewheel', this.collapseIf, this);
43638             
43639             this.fireEvent('expand', this);
43640         },
43641         
43642         restrictHeight : function()
43643         {
43644             this.list.alignTo(this.inputEl(), this.listAlign);
43645             this.list.alignTo(this.inputEl(), this.listAlign);
43646         },
43647         
43648         onViewOver : function(e, t)
43649         {
43650             if(this.inKeyMode){
43651                 return;
43652             }
43653             var item = this.view.findItemFromChild(t);
43654             
43655             if(item){
43656                 var index = this.view.indexOf(item);
43657                 this.select(index, false);
43658             }
43659         },
43660
43661         // private
43662         onViewClick : function(view, doFocus, el, e)
43663         {
43664             var index = this.view.getSelectedIndexes()[0];
43665             
43666             var r = this.store.getAt(index);
43667             
43668             if(r){
43669                 this.onSelect(r, index);
43670             }
43671             if(doFocus !== false && !this.blockFocus){
43672                 this.inputEl().focus();
43673             }
43674         },
43675         
43676         onViewMove : function(e, t)
43677         {
43678             this.inKeyMode = false;
43679         },
43680         
43681         select : function(index, scrollIntoView)
43682         {
43683             this.selectedIndex = index;
43684             this.view.select(index);
43685             if(scrollIntoView !== false){
43686                 var el = this.view.getNode(index);
43687                 if(el){
43688                     this.list.scrollChildIntoView(el, false);
43689                 }
43690             }
43691         },
43692         
43693         createList : function()
43694         {
43695             this.list = Roo.get(document.body).createChild({
43696                 tag: 'ul',
43697                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43698                 style: 'display:none'
43699             });
43700             
43701             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43702         },
43703         
43704         collapseIf : function(e)
43705         {
43706             var in_combo  = e.within(this.el);
43707             var in_list =  e.within(this.list);
43708             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43709             
43710             if (in_combo || in_list || is_list) {
43711                 return;
43712             }
43713             this.collapse();
43714         },
43715         
43716         onSelect : function(record, index)
43717         {
43718             if(this.fireEvent('beforeselect', this, record, index) !== false){
43719                 
43720                 this.setFlagClass(record.data.iso2);
43721                 this.setDialCode(record.data.dialCode);
43722                 this.hasFocus = false;
43723                 this.collapse();
43724                 this.fireEvent('select', this, record, index);
43725             }
43726         },
43727         
43728         flagEl : function()
43729         {
43730             var flag = this.el.select('div.flag',true).first();
43731             if(!flag){
43732                 return false;
43733             }
43734             return flag;
43735         },
43736         
43737         dialCodeHolderEl : function()
43738         {
43739             var d = this.el.select('input.dial-code-holder',true).first();
43740             if(!d){
43741                 return false;
43742             }
43743             return d;
43744         },
43745         
43746         setDialCode : function(v)
43747         {
43748             this.dialCodeHolder.dom.value = '+'+v;
43749         },
43750         
43751         setFlagClass : function(n)
43752         {
43753             this.flag.dom.className = 'flag '+n;
43754         },
43755         
43756         getValue : function()
43757         {
43758             var v = this.inputEl().getValue();
43759             if(this.dialCodeHolder) {
43760                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43761             }
43762             return v;
43763         },
43764         
43765         setValue : function(v)
43766         {
43767             var d = this.getDialCode(v);
43768             
43769             //invalid dial code
43770             if(v.length == 0 || !d || d.length == 0) {
43771                 if(this.rendered){
43772                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43773                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43774                 }
43775                 return;
43776             }
43777             
43778             //valid dial code
43779             this.setFlagClass(this.dialCodeMapping[d].iso2);
43780             this.setDialCode(d);
43781             this.inputEl().dom.value = v.replace('+'+d,'');
43782             this.hiddenEl().dom.value = this.getValue();
43783             
43784             this.validate();
43785         },
43786         
43787         getDialCode : function(v)
43788         {
43789             v = v ||  '';
43790             
43791             if (v.length == 0) {
43792                 return this.dialCodeHolder.dom.value;
43793             }
43794             
43795             var dialCode = "";
43796             if (v.charAt(0) != "+") {
43797                 return false;
43798             }
43799             var numericChars = "";
43800             for (var i = 1; i < v.length; i++) {
43801               var c = v.charAt(i);
43802               if (!isNaN(c)) {
43803                 numericChars += c;
43804                 if (this.dialCodeMapping[numericChars]) {
43805                   dialCode = v.substr(1, i);
43806                 }
43807                 if (numericChars.length == 4) {
43808                   break;
43809                 }
43810               }
43811             }
43812             return dialCode;
43813         },
43814         
43815         reset : function()
43816         {
43817             this.setValue(this.defaultDialCode);
43818             this.validate();
43819         },
43820         
43821         hiddenEl : function()
43822         {
43823             return this.el.select('input.hidden-tel-input',true).first();
43824         },
43825         
43826         // after setting val
43827         onKeyUp : function(e){
43828             this.setValue(this.getValue());
43829         },
43830         
43831         onKeyPress : function(e){
43832             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43833                 e.stopEvent();
43834             }
43835         }
43836         
43837 });
43838 /**
43839  * @class Roo.bootstrap.form.MoneyField
43840  * @extends Roo.bootstrap.form.ComboBox
43841  * Bootstrap MoneyField class
43842  * 
43843  * @constructor
43844  * Create a new MoneyField.
43845  * @param {Object} config Configuration options
43846  */
43847
43848 Roo.bootstrap.form.MoneyField = function(config) {
43849     
43850     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43851     
43852 };
43853
43854 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43855     
43856     /**
43857      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43858      */
43859     allowDecimals : true,
43860     /**
43861      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43862      */
43863     decimalSeparator : ".",
43864     /**
43865      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43866      */
43867     decimalPrecision : 0,
43868     /**
43869      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43870      */
43871     allowNegative : true,
43872     /**
43873      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43874      */
43875     allowZero: true,
43876     /**
43877      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43878      */
43879     minValue : Number.NEGATIVE_INFINITY,
43880     /**
43881      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43882      */
43883     maxValue : Number.MAX_VALUE,
43884     /**
43885      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43886      */
43887     minText : "The minimum value for this field is {0}",
43888     /**
43889      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43890      */
43891     maxText : "The maximum value for this field is {0}",
43892     /**
43893      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43894      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43895      */
43896     nanText : "{0} is not a valid number",
43897     /**
43898      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43899      */
43900     castInt : true,
43901     /**
43902      * @cfg {String} defaults currency of the MoneyField
43903      * value should be in lkey
43904      */
43905     defaultCurrency : false,
43906     /**
43907      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43908      */
43909     thousandsDelimiter : false,
43910     /**
43911      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43912      */
43913     max_length: false,
43914     
43915     inputlg : 9,
43916     inputmd : 9,
43917     inputsm : 9,
43918     inputxs : 6,
43919      /**
43920      * @cfg {Roo.data.Store} store  Store to lookup currency??
43921      */
43922     store : false,
43923     
43924     getAutoCreate : function()
43925     {
43926         var align = this.labelAlign || this.parentLabelAlign();
43927         
43928         var id = Roo.id();
43929
43930         var cfg = {
43931             cls: 'form-group',
43932             cn: []
43933         };
43934
43935         var input =  {
43936             tag: 'input',
43937             id : id,
43938             cls : 'form-control roo-money-amount-input',
43939             autocomplete: 'new-password'
43940         };
43941         
43942         var hiddenInput = {
43943             tag: 'input',
43944             type: 'hidden',
43945             id: Roo.id(),
43946             cls: 'hidden-number-input'
43947         };
43948         
43949         if(this.max_length) {
43950             input.maxlength = this.max_length; 
43951         }
43952         
43953         if (this.name) {
43954             hiddenInput.name = this.name;
43955         }
43956
43957         if (this.disabled) {
43958             input.disabled = true;
43959         }
43960
43961         var clg = 12 - this.inputlg;
43962         var cmd = 12 - this.inputmd;
43963         var csm = 12 - this.inputsm;
43964         var cxs = 12 - this.inputxs;
43965         
43966         var container = {
43967             tag : 'div',
43968             cls : 'row roo-money-field',
43969             cn : [
43970                 {
43971                     tag : 'div',
43972                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43973                     cn : [
43974                         {
43975                             tag : 'div',
43976                             cls: 'roo-select2-container input-group',
43977                             cn: [
43978                                 {
43979                                     tag : 'input',
43980                                     cls : 'form-control roo-money-currency-input',
43981                                     autocomplete: 'new-password',
43982                                     readOnly : 1,
43983                                     name : this.currencyName
43984                                 },
43985                                 {
43986                                     tag :'span',
43987                                     cls : 'input-group-addon',
43988                                     cn : [
43989                                         {
43990                                             tag: 'span',
43991                                             cls: 'caret'
43992                                         }
43993                                     ]
43994                                 }
43995                             ]
43996                         }
43997                     ]
43998                 },
43999                 {
44000                     tag : 'div',
44001                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44002                     cn : [
44003                         {
44004                             tag: 'div',
44005                             cls: this.hasFeedback ? 'has-feedback' : '',
44006                             cn: [
44007                                 input
44008                             ]
44009                         }
44010                     ]
44011                 }
44012             ]
44013             
44014         };
44015         
44016         if (this.fieldLabel.length) {
44017             var indicator = {
44018                 tag: 'i',
44019                 tooltip: 'This field is required'
44020             };
44021
44022             var label = {
44023                 tag: 'label',
44024                 'for':  id,
44025                 cls: 'control-label',
44026                 cn: []
44027             };
44028
44029             var label_text = {
44030                 tag: 'span',
44031                 html: this.fieldLabel
44032             };
44033
44034             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44035             label.cn = [
44036                 indicator,
44037                 label_text
44038             ];
44039
44040             if(this.indicatorpos == 'right') {
44041                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44042                 label.cn = [
44043                     label_text,
44044                     indicator
44045                 ];
44046             }
44047
44048             if(align == 'left') {
44049                 container = {
44050                     tag: 'div',
44051                     cn: [
44052                         container
44053                     ]
44054                 };
44055
44056                 if(this.labelWidth > 12){
44057                     label.style = "width: " + this.labelWidth + 'px';
44058                 }
44059                 if(this.labelWidth < 13 && this.labelmd == 0){
44060                     this.labelmd = this.labelWidth;
44061                 }
44062                 if(this.labellg > 0){
44063                     label.cls += ' col-lg-' + this.labellg;
44064                     input.cls += ' col-lg-' + (12 - this.labellg);
44065                 }
44066                 if(this.labelmd > 0){
44067                     label.cls += ' col-md-' + this.labelmd;
44068                     container.cls += ' col-md-' + (12 - this.labelmd);
44069                 }
44070                 if(this.labelsm > 0){
44071                     label.cls += ' col-sm-' + this.labelsm;
44072                     container.cls += ' col-sm-' + (12 - this.labelsm);
44073                 }
44074                 if(this.labelxs > 0){
44075                     label.cls += ' col-xs-' + this.labelxs;
44076                     container.cls += ' col-xs-' + (12 - this.labelxs);
44077                 }
44078             }
44079         }
44080
44081         cfg.cn = [
44082             label,
44083             container,
44084             hiddenInput
44085         ];
44086         
44087         var settings = this;
44088
44089         ['xs','sm','md','lg'].map(function(size){
44090             if (settings[size]) {
44091                 cfg.cls += ' col-' + size + '-' + settings[size];
44092             }
44093         });
44094         
44095         return cfg;
44096     },
44097     
44098     initEvents : function()
44099     {
44100         this.indicator = this.indicatorEl();
44101         
44102         this.initCurrencyEvent();
44103         
44104         this.initNumberEvent();
44105     },
44106     
44107     initCurrencyEvent : function()
44108     {
44109         if (!this.store) {
44110             throw "can not find store for combo";
44111         }
44112         
44113         this.store = Roo.factory(this.store, Roo.data);
44114         this.store.parent = this;
44115         
44116         this.createList();
44117         
44118         this.triggerEl = this.el.select('.input-group-addon', true).first();
44119         
44120         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44121         
44122         var _this = this;
44123         
44124         (function(){
44125             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44126             _this.list.setWidth(lw);
44127         }).defer(100);
44128         
44129         this.list.on('mouseover', this.onViewOver, this);
44130         this.list.on('mousemove', this.onViewMove, this);
44131         this.list.on('scroll', this.onViewScroll, this);
44132         
44133         if(!this.tpl){
44134             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44135         }
44136         
44137         this.view = new Roo.View(this.list, this.tpl, {
44138             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44139         });
44140         
44141         this.view.on('click', this.onViewClick, this);
44142         
44143         this.store.on('beforeload', this.onBeforeLoad, this);
44144         this.store.on('load', this.onLoad, this);
44145         this.store.on('loadexception', this.onLoadException, this);
44146         
44147         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44148             "up" : function(e){
44149                 this.inKeyMode = true;
44150                 this.selectPrev();
44151             },
44152
44153             "down" : function(e){
44154                 if(!this.isExpanded()){
44155                     this.onTriggerClick();
44156                 }else{
44157                     this.inKeyMode = true;
44158                     this.selectNext();
44159                 }
44160             },
44161
44162             "enter" : function(e){
44163                 this.collapse();
44164                 
44165                 if(this.fireEvent("specialkey", this, e)){
44166                     this.onViewClick(false);
44167                 }
44168                 
44169                 return true;
44170             },
44171
44172             "esc" : function(e){
44173                 this.collapse();
44174             },
44175
44176             "tab" : function(e){
44177                 this.collapse();
44178                 
44179                 if(this.fireEvent("specialkey", this, e)){
44180                     this.onViewClick(false);
44181                 }
44182                 
44183                 return true;
44184             },
44185
44186             scope : this,
44187
44188             doRelay : function(foo, bar, hname){
44189                 if(hname == 'down' || this.scope.isExpanded()){
44190                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44191                 }
44192                 return true;
44193             },
44194
44195             forceKeyDown: true
44196         });
44197         
44198         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44199         
44200     },
44201     
44202     initNumberEvent : function(e)
44203     {
44204         this.inputEl().on("keydown" , this.fireKey,  this);
44205         this.inputEl().on("focus", this.onFocus,  this);
44206         this.inputEl().on("blur", this.onBlur,  this);
44207         
44208         this.inputEl().relayEvent('keyup', this);
44209         
44210         if(this.indicator){
44211             this.indicator.addClass('invisible');
44212         }
44213  
44214         this.originalValue = this.getValue();
44215         
44216         if(this.validationEvent == 'keyup'){
44217             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44218             this.inputEl().on('keyup', this.filterValidation, this);
44219         }
44220         else if(this.validationEvent !== false){
44221             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44222         }
44223         
44224         if(this.selectOnFocus){
44225             this.on("focus", this.preFocus, this);
44226             
44227         }
44228         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44229             this.inputEl().on("keypress", this.filterKeys, this);
44230         } else {
44231             this.inputEl().relayEvent('keypress', this);
44232         }
44233         
44234         var allowed = "0123456789";
44235         
44236         if(this.allowDecimals){
44237             allowed += this.decimalSeparator;
44238         }
44239         
44240         if(this.allowNegative){
44241             allowed += "-";
44242         }
44243         
44244         if(this.thousandsDelimiter) {
44245             allowed += ",";
44246         }
44247         
44248         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44249         
44250         var keyPress = function(e){
44251             
44252             var k = e.getKey();
44253             
44254             var c = e.getCharCode();
44255             
44256             if(
44257                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44258                     allowed.indexOf(String.fromCharCode(c)) === -1
44259             ){
44260                 e.stopEvent();
44261                 return;
44262             }
44263             
44264             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44265                 return;
44266             }
44267             
44268             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44269                 e.stopEvent();
44270             }
44271         };
44272         
44273         this.inputEl().on("keypress", keyPress, this);
44274         
44275     },
44276     
44277     onTriggerClick : function(e)
44278     {   
44279         if(this.disabled){
44280             return;
44281         }
44282         
44283         this.page = 0;
44284         this.loadNext = false;
44285         
44286         if(this.isExpanded()){
44287             this.collapse();
44288             return;
44289         }
44290         
44291         this.hasFocus = true;
44292         
44293         if(this.triggerAction == 'all') {
44294             this.doQuery(this.allQuery, true);
44295             return;
44296         }
44297         
44298         this.doQuery(this.getRawValue());
44299     },
44300     
44301     getCurrency : function()
44302     {   
44303         var v = this.currencyEl().getValue();
44304         
44305         return v;
44306     },
44307     
44308     restrictHeight : function()
44309     {
44310         this.list.alignTo(this.currencyEl(), this.listAlign);
44311         this.list.alignTo(this.currencyEl(), this.listAlign);
44312     },
44313     
44314     onViewClick : function(view, doFocus, el, e)
44315     {
44316         var index = this.view.getSelectedIndexes()[0];
44317         
44318         var r = this.store.getAt(index);
44319         
44320         if(r){
44321             this.onSelect(r, index);
44322         }
44323     },
44324     
44325     onSelect : function(record, index){
44326         
44327         if(this.fireEvent('beforeselect', this, record, index) !== false){
44328         
44329             this.setFromCurrencyData(index > -1 ? record.data : false);
44330             
44331             this.collapse();
44332             
44333             this.fireEvent('select', this, record, index);
44334         }
44335     },
44336     
44337     setFromCurrencyData : function(o)
44338     {
44339         var currency = '';
44340         
44341         this.lastCurrency = o;
44342         
44343         if (this.currencyField) {
44344             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44345         } else {
44346             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44347         }
44348         
44349         this.lastSelectionText = currency;
44350         
44351         //setting default currency
44352         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44353             this.setCurrency(this.defaultCurrency);
44354             return;
44355         }
44356         
44357         this.setCurrency(currency);
44358     },
44359     
44360     setFromData : function(o)
44361     {
44362         var c = {};
44363         
44364         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44365         
44366         this.setFromCurrencyData(c);
44367         
44368         var value = '';
44369         
44370         if (this.name) {
44371             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44372         } else {
44373             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44374         }
44375         
44376         this.setValue(value);
44377         
44378     },
44379     
44380     setCurrency : function(v)
44381     {   
44382         this.currencyValue = v;
44383         
44384         if(this.rendered){
44385             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44386             this.validate();
44387         }
44388     },
44389     
44390     setValue : function(v)
44391     {
44392         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44393         
44394         this.value = v;
44395         
44396         if(this.rendered){
44397             
44398             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44399             
44400             this.inputEl().dom.value = (v == '') ? '' :
44401                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44402             
44403             if(!this.allowZero && v === '0') {
44404                 this.hiddenEl().dom.value = '';
44405                 this.inputEl().dom.value = '';
44406             }
44407             
44408             this.validate();
44409         }
44410     },
44411     
44412     getRawValue : function()
44413     {
44414         var v = this.inputEl().getValue();
44415         
44416         return v;
44417     },
44418     
44419     getValue : function()
44420     {
44421         return this.fixPrecision(this.parseValue(this.getRawValue()));
44422     },
44423     
44424     parseValue : function(value)
44425     {
44426         if(this.thousandsDelimiter) {
44427             value += "";
44428             r = new RegExp(",", "g");
44429             value = value.replace(r, "");
44430         }
44431         
44432         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44433         return isNaN(value) ? '' : value;
44434         
44435     },
44436     
44437     fixPrecision : function(value)
44438     {
44439         if(this.thousandsDelimiter) {
44440             value += "";
44441             r = new RegExp(",", "g");
44442             value = value.replace(r, "");
44443         }
44444         
44445         var nan = isNaN(value);
44446         
44447         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44448             return nan ? '' : value;
44449         }
44450         return parseFloat(value).toFixed(this.decimalPrecision);
44451     },
44452     
44453     decimalPrecisionFcn : function(v)
44454     {
44455         return Math.floor(v);
44456     },
44457     
44458     validateValue : function(value)
44459     {
44460         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44461             return false;
44462         }
44463         
44464         var num = this.parseValue(value);
44465         
44466         if(isNaN(num)){
44467             this.markInvalid(String.format(this.nanText, value));
44468             return false;
44469         }
44470         
44471         if(num < this.minValue){
44472             this.markInvalid(String.format(this.minText, this.minValue));
44473             return false;
44474         }
44475         
44476         if(num > this.maxValue){
44477             this.markInvalid(String.format(this.maxText, this.maxValue));
44478             return false;
44479         }
44480         
44481         return true;
44482     },
44483     
44484     validate : function()
44485     {
44486         if(this.disabled || this.allowBlank){
44487             this.markValid();
44488             return true;
44489         }
44490         
44491         var currency = this.getCurrency();
44492         
44493         if(this.validateValue(this.getRawValue()) && currency.length){
44494             this.markValid();
44495             return true;
44496         }
44497         
44498         this.markInvalid();
44499         return false;
44500     },
44501     
44502     getName: function()
44503     {
44504         return this.name;
44505     },
44506     
44507     beforeBlur : function()
44508     {
44509         if(!this.castInt){
44510             return;
44511         }
44512         
44513         var v = this.parseValue(this.getRawValue());
44514         
44515         if(v || v == 0){
44516             this.setValue(v);
44517         }
44518     },
44519     
44520     onBlur : function()
44521     {
44522         this.beforeBlur();
44523         
44524         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44525             //this.el.removeClass(this.focusClass);
44526         }
44527         
44528         this.hasFocus = false;
44529         
44530         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44531             this.validate();
44532         }
44533         
44534         var v = this.getValue();
44535         
44536         if(String(v) !== String(this.startValue)){
44537             this.fireEvent('change', this, v, this.startValue);
44538         }
44539         
44540         this.fireEvent("blur", this);
44541     },
44542     
44543     inputEl : function()
44544     {
44545         return this.el.select('.roo-money-amount-input', true).first();
44546     },
44547     
44548     currencyEl : function()
44549     {
44550         return this.el.select('.roo-money-currency-input', true).first();
44551     },
44552     
44553     hiddenEl : function()
44554     {
44555         return this.el.select('input.hidden-number-input',true).first();
44556     }
44557     
44558 });/**
44559  * @class Roo.bootstrap.BezierSignature
44560  * @extends Roo.bootstrap.Component
44561  * Bootstrap BezierSignature class
44562  * This script refer to:
44563  *    Title: Signature Pad
44564  *    Author: szimek
44565  *    Availability: https://github.com/szimek/signature_pad
44566  *
44567  * @constructor
44568  * Create a new BezierSignature
44569  * @param {Object} config The config object
44570  */
44571
44572 Roo.bootstrap.BezierSignature = function(config){
44573     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44574     this.addEvents({
44575         "resize" : true
44576     });
44577 };
44578
44579 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44580 {
44581      
44582     curve_data: [],
44583     
44584     is_empty: true,
44585     
44586     mouse_btn_down: true,
44587     
44588     /**
44589      * @cfg {int} canvas height
44590      */
44591     canvas_height: '200px',
44592     
44593     /**
44594      * @cfg {float|function} Radius of a single dot.
44595      */ 
44596     dot_size: false,
44597     
44598     /**
44599      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44600      */
44601     min_width: 0.5,
44602     
44603     /**
44604      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44605      */
44606     max_width: 2.5,
44607     
44608     /**
44609      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44610      */
44611     throttle: 16,
44612     
44613     /**
44614      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44615      */
44616     min_distance: 5,
44617     
44618     /**
44619      * @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.
44620      */
44621     bg_color: 'rgba(0, 0, 0, 0)',
44622     
44623     /**
44624      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44625      */
44626     dot_color: 'black',
44627     
44628     /**
44629      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44630      */ 
44631     velocity_filter_weight: 0.7,
44632     
44633     /**
44634      * @cfg {function} Callback when stroke begin. 
44635      */
44636     onBegin: false,
44637     
44638     /**
44639      * @cfg {function} Callback when stroke end.
44640      */
44641     onEnd: false,
44642     
44643     getAutoCreate : function()
44644     {
44645         var cls = 'roo-signature column';
44646         
44647         if(this.cls){
44648             cls += ' ' + this.cls;
44649         }
44650         
44651         var col_sizes = [
44652             'lg',
44653             'md',
44654             'sm',
44655             'xs'
44656         ];
44657         
44658         for(var i = 0; i < col_sizes.length; i++) {
44659             if(this[col_sizes[i]]) {
44660                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44661             }
44662         }
44663         
44664         var cfg = {
44665             tag: 'div',
44666             cls: cls,
44667             cn: [
44668                 {
44669                     tag: 'div',
44670                     cls: 'roo-signature-body',
44671                     cn: [
44672                         {
44673                             tag: 'canvas',
44674                             cls: 'roo-signature-body-canvas',
44675                             height: this.canvas_height,
44676                             width: this.canvas_width
44677                         }
44678                     ]
44679                 },
44680                 {
44681                     tag: 'input',
44682                     type: 'file',
44683                     style: 'display: none'
44684                 }
44685             ]
44686         };
44687         
44688         return cfg;
44689     },
44690     
44691     initEvents: function() 
44692     {
44693         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44694         
44695         var canvas = this.canvasEl();
44696         
44697         // mouse && touch event swapping...
44698         canvas.dom.style.touchAction = 'none';
44699         canvas.dom.style.msTouchAction = 'none';
44700         
44701         this.mouse_btn_down = false;
44702         canvas.on('mousedown', this._handleMouseDown, this);
44703         canvas.on('mousemove', this._handleMouseMove, this);
44704         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44705         
44706         if (window.PointerEvent) {
44707             canvas.on('pointerdown', this._handleMouseDown, this);
44708             canvas.on('pointermove', this._handleMouseMove, this);
44709             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44710         }
44711         
44712         if ('ontouchstart' in window) {
44713             canvas.on('touchstart', this._handleTouchStart, this);
44714             canvas.on('touchmove', this._handleTouchMove, this);
44715             canvas.on('touchend', this._handleTouchEnd, this);
44716         }
44717         
44718         Roo.EventManager.onWindowResize(this.resize, this, true);
44719         
44720         // file input event
44721         this.fileEl().on('change', this.uploadImage, this);
44722         
44723         this.clear();
44724         
44725         this.resize();
44726     },
44727     
44728     resize: function(){
44729         
44730         var canvas = this.canvasEl().dom;
44731         var ctx = this.canvasElCtx();
44732         var img_data = false;
44733         
44734         if(canvas.width > 0) {
44735             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44736         }
44737         // setting canvas width will clean img data
44738         canvas.width = 0;
44739         
44740         var style = window.getComputedStyle ? 
44741             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44742             
44743         var padding_left = parseInt(style.paddingLeft) || 0;
44744         var padding_right = parseInt(style.paddingRight) || 0;
44745         
44746         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44747         
44748         if(img_data) {
44749             ctx.putImageData(img_data, 0, 0);
44750         }
44751     },
44752     
44753     _handleMouseDown: function(e)
44754     {
44755         if (e.browserEvent.which === 1) {
44756             this.mouse_btn_down = true;
44757             this.strokeBegin(e);
44758         }
44759     },
44760     
44761     _handleMouseMove: function (e)
44762     {
44763         if (this.mouse_btn_down) {
44764             this.strokeMoveUpdate(e);
44765         }
44766     },
44767     
44768     _handleMouseUp: function (e)
44769     {
44770         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44771             this.mouse_btn_down = false;
44772             this.strokeEnd(e);
44773         }
44774     },
44775     
44776     _handleTouchStart: function (e) {
44777         
44778         e.preventDefault();
44779         if (e.browserEvent.targetTouches.length === 1) {
44780             // var touch = e.browserEvent.changedTouches[0];
44781             // this.strokeBegin(touch);
44782             
44783              this.strokeBegin(e); // assume e catching the correct xy...
44784         }
44785     },
44786     
44787     _handleTouchMove: function (e) {
44788         e.preventDefault();
44789         // var touch = event.targetTouches[0];
44790         // _this._strokeMoveUpdate(touch);
44791         this.strokeMoveUpdate(e);
44792     },
44793     
44794     _handleTouchEnd: function (e) {
44795         var wasCanvasTouched = e.target === this.canvasEl().dom;
44796         if (wasCanvasTouched) {
44797             e.preventDefault();
44798             // var touch = event.changedTouches[0];
44799             // _this._strokeEnd(touch);
44800             this.strokeEnd(e);
44801         }
44802     },
44803     
44804     reset: function () {
44805         this._lastPoints = [];
44806         this._lastVelocity = 0;
44807         this._lastWidth = (this.min_width + this.max_width) / 2;
44808         this.canvasElCtx().fillStyle = this.dot_color;
44809     },
44810     
44811     strokeMoveUpdate: function(e)
44812     {
44813         this.strokeUpdate(e);
44814         
44815         if (this.throttle) {
44816             this.throttleStroke(this.strokeUpdate, this.throttle);
44817         }
44818         else {
44819             this.strokeUpdate(e);
44820         }
44821     },
44822     
44823     strokeBegin: function(e)
44824     {
44825         var newPointGroup = {
44826             color: this.dot_color,
44827             points: []
44828         };
44829         
44830         if (typeof this.onBegin === 'function') {
44831             this.onBegin(e);
44832         }
44833         
44834         this.curve_data.push(newPointGroup);
44835         this.reset();
44836         this.strokeUpdate(e);
44837     },
44838     
44839     strokeUpdate: function(e)
44840     {
44841         var rect = this.canvasEl().dom.getBoundingClientRect();
44842         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44843         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44844         var lastPoints = lastPointGroup.points;
44845         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44846         var isLastPointTooClose = lastPoint
44847             ? point.distanceTo(lastPoint) <= this.min_distance
44848             : false;
44849         var color = lastPointGroup.color;
44850         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44851             var curve = this.addPoint(point);
44852             if (!lastPoint) {
44853                 this.drawDot({color: color, point: point});
44854             }
44855             else if (curve) {
44856                 this.drawCurve({color: color, curve: curve});
44857             }
44858             lastPoints.push({
44859                 time: point.time,
44860                 x: point.x,
44861                 y: point.y
44862             });
44863         }
44864     },
44865     
44866     strokeEnd: function(e)
44867     {
44868         this.strokeUpdate(e);
44869         if (typeof this.onEnd === 'function') {
44870             this.onEnd(e);
44871         }
44872     },
44873     
44874     addPoint:  function (point) {
44875         var _lastPoints = this._lastPoints;
44876         _lastPoints.push(point);
44877         if (_lastPoints.length > 2) {
44878             if (_lastPoints.length === 3) {
44879                 _lastPoints.unshift(_lastPoints[0]);
44880             }
44881             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44882             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44883             _lastPoints.shift();
44884             return curve;
44885         }
44886         return null;
44887     },
44888     
44889     calculateCurveWidths: function (startPoint, endPoint) {
44890         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44891             (1 - this.velocity_filter_weight) * this._lastVelocity;
44892
44893         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44894         var widths = {
44895             end: newWidth,
44896             start: this._lastWidth
44897         };
44898         
44899         this._lastVelocity = velocity;
44900         this._lastWidth = newWidth;
44901         return widths;
44902     },
44903     
44904     drawDot: function (_a) {
44905         var color = _a.color, point = _a.point;
44906         var ctx = this.canvasElCtx();
44907         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44908         ctx.beginPath();
44909         this.drawCurveSegment(point.x, point.y, width);
44910         ctx.closePath();
44911         ctx.fillStyle = color;
44912         ctx.fill();
44913     },
44914     
44915     drawCurve: function (_a) {
44916         var color = _a.color, curve = _a.curve;
44917         var ctx = this.canvasElCtx();
44918         var widthDelta = curve.endWidth - curve.startWidth;
44919         var drawSteps = Math.floor(curve.length()) * 2;
44920         ctx.beginPath();
44921         ctx.fillStyle = color;
44922         for (var i = 0; i < drawSteps; i += 1) {
44923         var t = i / drawSteps;
44924         var tt = t * t;
44925         var ttt = tt * t;
44926         var u = 1 - t;
44927         var uu = u * u;
44928         var uuu = uu * u;
44929         var x = uuu * curve.startPoint.x;
44930         x += 3 * uu * t * curve.control1.x;
44931         x += 3 * u * tt * curve.control2.x;
44932         x += ttt * curve.endPoint.x;
44933         var y = uuu * curve.startPoint.y;
44934         y += 3 * uu * t * curve.control1.y;
44935         y += 3 * u * tt * curve.control2.y;
44936         y += ttt * curve.endPoint.y;
44937         var width = curve.startWidth + ttt * widthDelta;
44938         this.drawCurveSegment(x, y, width);
44939         }
44940         ctx.closePath();
44941         ctx.fill();
44942     },
44943     
44944     drawCurveSegment: function (x, y, width) {
44945         var ctx = this.canvasElCtx();
44946         ctx.moveTo(x, y);
44947         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44948         this.is_empty = false;
44949     },
44950     
44951     clear: function()
44952     {
44953         var ctx = this.canvasElCtx();
44954         var canvas = this.canvasEl().dom;
44955         ctx.fillStyle = this.bg_color;
44956         ctx.clearRect(0, 0, canvas.width, canvas.height);
44957         ctx.fillRect(0, 0, canvas.width, canvas.height);
44958         this.curve_data = [];
44959         this.reset();
44960         this.is_empty = true;
44961     },
44962     
44963     fileEl: function()
44964     {
44965         return  this.el.select('input',true).first();
44966     },
44967     
44968     canvasEl: function()
44969     {
44970         return this.el.select('canvas',true).first();
44971     },
44972     
44973     canvasElCtx: function()
44974     {
44975         return this.el.select('canvas',true).first().dom.getContext('2d');
44976     },
44977     
44978     getImage: function(type)
44979     {
44980         if(this.is_empty) {
44981             return false;
44982         }
44983         
44984         // encryption ?
44985         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44986     },
44987     
44988     drawFromImage: function(img_src)
44989     {
44990         var img = new Image();
44991         
44992         img.onload = function(){
44993             this.canvasElCtx().drawImage(img, 0, 0);
44994         }.bind(this);
44995         
44996         img.src = img_src;
44997         
44998         this.is_empty = false;
44999     },
45000     
45001     selectImage: function()
45002     {
45003         this.fileEl().dom.click();
45004     },
45005     
45006     uploadImage: function(e)
45007     {
45008         var reader = new FileReader();
45009         
45010         reader.onload = function(e){
45011             var img = new Image();
45012             img.onload = function(){
45013                 this.reset();
45014                 this.canvasElCtx().drawImage(img, 0, 0);
45015             }.bind(this);
45016             img.src = e.target.result;
45017         }.bind(this);
45018         
45019         reader.readAsDataURL(e.target.files[0]);
45020     },
45021     
45022     // Bezier Point Constructor
45023     Point: (function () {
45024         function Point(x, y, time) {
45025             this.x = x;
45026             this.y = y;
45027             this.time = time || Date.now();
45028         }
45029         Point.prototype.distanceTo = function (start) {
45030             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45031         };
45032         Point.prototype.equals = function (other) {
45033             return this.x === other.x && this.y === other.y && this.time === other.time;
45034         };
45035         Point.prototype.velocityFrom = function (start) {
45036             return this.time !== start.time
45037             ? this.distanceTo(start) / (this.time - start.time)
45038             : 0;
45039         };
45040         return Point;
45041     }()),
45042     
45043     
45044     // Bezier Constructor
45045     Bezier: (function () {
45046         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45047             this.startPoint = startPoint;
45048             this.control2 = control2;
45049             this.control1 = control1;
45050             this.endPoint = endPoint;
45051             this.startWidth = startWidth;
45052             this.endWidth = endWidth;
45053         }
45054         Bezier.fromPoints = function (points, widths, scope) {
45055             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45056             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45057             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45058         };
45059         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45060             var dx1 = s1.x - s2.x;
45061             var dy1 = s1.y - s2.y;
45062             var dx2 = s2.x - s3.x;
45063             var dy2 = s2.y - s3.y;
45064             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45065             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45066             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45067             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45068             var dxm = m1.x - m2.x;
45069             var dym = m1.y - m2.y;
45070             var k = l2 / (l1 + l2);
45071             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45072             var tx = s2.x - cm.x;
45073             var ty = s2.y - cm.y;
45074             return {
45075                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45076                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45077             };
45078         };
45079         Bezier.prototype.length = function () {
45080             var steps = 10;
45081             var length = 0;
45082             var px;
45083             var py;
45084             for (var i = 0; i <= steps; i += 1) {
45085                 var t = i / steps;
45086                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45087                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45088                 if (i > 0) {
45089                     var xdiff = cx - px;
45090                     var ydiff = cy - py;
45091                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45092                 }
45093                 px = cx;
45094                 py = cy;
45095             }
45096             return length;
45097         };
45098         Bezier.prototype.point = function (t, start, c1, c2, end) {
45099             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45100             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45101             + (3.0 * c2 * (1.0 - t) * t * t)
45102             + (end * t * t * t);
45103         };
45104         return Bezier;
45105     }()),
45106     
45107     throttleStroke: function(fn, wait) {
45108       if (wait === void 0) { wait = 250; }
45109       var previous = 0;
45110       var timeout = null;
45111       var result;
45112       var storedContext;
45113       var storedArgs;
45114       var later = function () {
45115           previous = Date.now();
45116           timeout = null;
45117           result = fn.apply(storedContext, storedArgs);
45118           if (!timeout) {
45119               storedContext = null;
45120               storedArgs = [];
45121           }
45122       };
45123       return function wrapper() {
45124           var args = [];
45125           for (var _i = 0; _i < arguments.length; _i++) {
45126               args[_i] = arguments[_i];
45127           }
45128           var now = Date.now();
45129           var remaining = wait - (now - previous);
45130           storedContext = this;
45131           storedArgs = args;
45132           if (remaining <= 0 || remaining > wait) {
45133               if (timeout) {
45134                   clearTimeout(timeout);
45135                   timeout = null;
45136               }
45137               previous = now;
45138               result = fn.apply(storedContext, storedArgs);
45139               if (!timeout) {
45140                   storedContext = null;
45141                   storedArgs = [];
45142               }
45143           }
45144           else if (!timeout) {
45145               timeout = window.setTimeout(later, remaining);
45146           }
45147           return result;
45148       };
45149   }
45150   
45151 });
45152
45153  
45154
45155  // old names for form elements
45156 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
45157 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
45158 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
45159 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
45160 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
45161 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
45162 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
45163 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
45164 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
45165 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
45166 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
45167 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
45168 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
45169 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
45170 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
45171 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
45172 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
45173 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
45174 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
45175 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
45176 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
45177 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
45178 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
45179 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
45180 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
45181 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
45182
45183 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
45184 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45185
45186 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
45187 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
45188
45189 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
45190 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45191 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
45192 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
45193